Skip to content

Commit

Permalink
Merge pull request #18 from AugustoL/feat/dynamic-proxy
Browse files Browse the repository at this point in the history
Dynamic Proxy
  • Loading branch information
Augusto Lemble authored Jul 10, 2019
2 parents e48628c + 683a3fa commit 8b424ac
Show file tree
Hide file tree
Showing 16 changed files with 272 additions and 217 deletions.
14 changes: 9 additions & 5 deletions contracts/ERC827/ERC827.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
pragma solidity ^0.5.2;

import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "./ERC827Proxy.sol";
import "../utils/Create2.sol";


/**
Expand All @@ -15,13 +15,14 @@ import "./ERC827Proxy.sol";
*/
contract ERC827 is ERC20 {

ERC827Proxy public proxy;
bytes public proxyBytecode;
mapping(address => uint256) public nonces;

/**
* @dev Constructor
*/
constructor() public {
proxy = new ERC827Proxy();
constructor(bytes memory _proxyBytecode) public {
proxyBytecode = _proxyBytecode;
}

/**
Expand Down Expand Up @@ -85,11 +86,14 @@ contract ERC827 is ERC20 {
* @param _data ABI-encoded contract call to call `_to` address.
*/
function _call(address _to, bytes memory _data) internal {
bytes32 salt = keccak256(abi.encodePacked(msg.sender, _to, nonces[msg.sender]));
address proxy = Create2.deploy(salt, proxyBytecode);
// solium-disable-next-line security/no-call-value, no-unused-vars
(bool success, bytes memory data) = address(proxy).call.value(msg.value)(
abi.encodeWithSelector(proxy.callContractFunctionSignature(), _to, _data)
abi.encodeWithSelector(bytes4(keccak256("callContract(address,bytes)")), _to, _data)
);
require(success, "Call to external contract failed");
nonces[msg.sender].add(1);
}

}
2 changes: 1 addition & 1 deletion contracts/ERC827/ERC827Migratable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract ERC827Migratable is ERC827, Ownable {
* be migrated.
* @param _erc20Token The address of the erc20 token to be migrated
*/
constructor(address _erc20Token) public {
constructor(address _erc20Token, bytes memory proxyBytecode) ERC827(proxyBytecode) public {
require(_erc20Token != address(0), "Incorrect ERC20 token address");
erc20Token = IERC20(_erc20Token);
}
Expand Down
14 changes: 0 additions & 14 deletions contracts/ERC827/ERC827Mock.sol

This file was deleted.

15 changes: 9 additions & 6 deletions contracts/ERC827/ERC827Proxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

pragma solidity ^0.5.2;

import "./IERC827.sol";


/**
* @title ERC827Proxy
Expand All @@ -11,16 +13,13 @@ pragma solidity ^0.5.2;
*/
contract ERC827Proxy {

address public token;
bytes4 public callContractFunctionSignature = bytes4(
keccak256("callContract(address,bytes)")
);
IERC827 public token;

/**
* @dev constructor
*/
constructor() public {
token = address(msg.sender);
token = IERC827(msg.sender);
}

/**
Expand All @@ -36,8 +35,12 @@ contract ERC827Proxy {
"Proxy only can execute calls from the token contract"
);
// solium-disable-next-line security/no-call-value, no-unused-vars
(bool success, bytes memory data) = _target .call.value(msg.value)(_data);
(bool success, bytes memory data) = _target.call.value(msg.value)(_data);
require(success, "Proxy call failed");
require(token.balanceOf(address(this)) == 0, "Cant end proxy call with token balance in proxy");
require(address(this).balance == 0, "Cant end proxy call with eth balance in proxy");

selfdestruct(address(0));
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";


// mock class using ERC20 Token
contract ERC20TokenMock is ERC20 {
contract ERC20Mock is ERC20 {

constructor(address initialAccount, uint256 initialBalance) public {
_mint(initialAccount, initialBalance);
Expand Down
14 changes: 14 additions & 0 deletions contracts/mocks/ERC827Mock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pragma solidity ^0.5.0;


import "../ERC827/ERC827.sol";


// mock class using ERC827 Token
contract ERC827Mock is ERC827 {

constructor(address initialAccount, uint256 initialBalance, bytes memory proxyBytecode) public ERC827(proxyBytecode) {
_mint(initialAccount, initialBalance);
}

}
47 changes: 47 additions & 0 deletions contracts/mocks/ERC827Receiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
pragma solidity ^0.5.0;

import "../ERC827/ERC827.sol";
import "../utils/Create2.sol";

contract ERC827Receiver {

event Show(bytes32 b32, uint256 number, string text, uint256 value);
event TokensTransfered(address from, uint256 amount);

function showMessage(bytes32 message, uint256 number, string memory text)
public payable returns (bool)
{
emit Show(message, number, text, msg.value);
return true;
}

function fail() public {
revert("ERC827Receiver function failed");
}

function callContarct(address to, bytes memory data) public returns (bool) {
// solium-disable-next-line security/no-low-level-calls, no-unused-vars
(bool success, bytes memory _data) = to.call(data);
require(success, "ERC827Receiver callContarct function failed");
return true;
}

function receiveTokens(address sender, address token) public {
uint256 allowance = ERC827(token).allowance(sender, address(this));
ERC827(token).transferFrom(sender, address(this), allowance);
emit TokensTransfered(sender, allowance);
}

function receiveVerifiedTokens(address sender, ERC827 token) public {
address proxy = Create2.computeAddress(
address(token),
keccak256(abi.encodePacked(sender, address(this), token.nonces(sender))),
token.proxyBytecode()
);
require(msg.sender == proxy, "ERC827Receiver: Sender invalid");
uint256 allowance = ERC827(token).allowance(sender, address(this));
ERC827(token).transferFrom(sender, address(this), allowance);
emit TokensTransfered(sender, allowance);
}

}
14 changes: 0 additions & 14 deletions contracts/mocks/ERC827TokenMock.sol

This file was deleted.

39 changes: 0 additions & 39 deletions contracts/mocks/MessageHelper.sol

This file was deleted.

63 changes: 63 additions & 0 deletions contracts/utils/Create2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
pragma solidity ^0.5.0;

/**
* @title Create2
*
* @dev Utility library that focus on the use of CREATE2 EVM opcode for
* contracts deployment. It also provides a function to precompute the address
* where the smart contracts with the specified salt and bytecode would be
* deployed.
*/
library Create2 {

// Event triggered when a contract is deployed
event Create2Deployed(address addr, bytes32 salt);

/**
* @dev Deploy contract with CREATE2
* @param salt The salt used to the contract address computation
* @param code The bytecode of of the contract to be deployed
*/
function deploy(bytes32 salt, bytes memory code) internal returns (address) {
address addr = _deploy(salt, code);
emit Create2Deployed(addr, salt);
return addr;
}

/**
* @dev Function to compute the address of a contract created with CREATE2.
* @param deployer the address of the contract that will deploy the contract
* @param salt The salt used to the contract address computation
* @param code The bytecode of the contract to be deployed
* @return the computed address of the smart contract.
*/
function computeAddress(
address deployer, bytes32 salt, bytes memory code
) internal view returns (address) {
bytes32 codeHash = keccak256(code);
bytes32 _data = keccak256(
abi.encodePacked(bytes1(0xff), deployer, salt, codeHash)
);
return address(bytes20(_data << 96));
}

/**
* @dev Internal function to deploy contract with CREATE2
* @param _salt The salt used to the contract address computation
* @param _code The bytecode of the contract to be deployed
*/
function _deploy(
bytes32 _salt, bytes memory _code
) private returns(address) {
address _addr;
// solhint-disable-next-line no-inline-assembly
assembly {
_addr := create2(0, add(_code, 0x20), mload(_code), _salt)
if iszero(extcodesize(_addr)) {
revert(0, 0)
}
}
return _addr;
}

}
9 changes: 5 additions & 4 deletions test/ERC827/ERC20Migration.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

import EVMRevert from '../helpers/EVMRevert';
var ERC20TokenMock = artifacts.require('ERC20TokenMock');
var ERC827Migratable = artifacts.require('ERC827Migratable');
const ERC20Mock = artifacts.require('ERC20Mock');
const ERC827Migratable = artifacts.require('ERC827Migratable');
const ERC827Proxy = artifacts.require('ERC827Proxy');

require('chai').use(require('chai-as-promised')).should();
const assert = require('chai').assert;
Expand All @@ -10,8 +11,8 @@ contract('ERC827Migratable', function (accounts) {
let erc20Token, erc827Token;

beforeEach(async function () {
erc20Token = await ERC20TokenMock.new(accounts[1], 100);
erc827Token = await ERC827Migratable.new(erc20Token.address);
erc20Token = await ERC20Mock.new(accounts[1], 100);
erc827Token = await ERC827Migratable.new(erc20Token.address, ERC827Proxy.bytecode);
await erc20Token.transfer(accounts[2], 10, { from: accounts[1] });
await erc20Token.transfer(accounts[3], 20, { from: accounts[1] });
await erc20Token.transfer(accounts[4], 30, { from: accounts[1] });
Expand Down
Loading

0 comments on commit 8b424ac

Please sign in to comment.