// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.6.12;
interface DSAuthority {
function canCall(
address src,
address dst,
bytes4 sig
) external view returns (bool);
}
contract DSAuthEvents {
event LogSetAuthority(address indexed authority);
event LogSetOwner(address indexed owner);
}
contract DSAuth is DSAuthEvents {
DSAuthority public authority;
address public owner;
constructor() public {
owner = msg.sender;
emit LogSetOwner(msg.sender);
}
function setOwner(address owner_) external auth {
require(owner != address(0),"ERR_ZERO_ADDRESS");
owner = owner_;
emit LogSetOwner(owner);
}
function setAuthority(DSAuthority authority_) external auth {
authority = authority_;
emit LogSetAuthority(address(authority));
}
modifier auth() {
require(isAuthorized(msg.sender, msg.sig), "ds-auth-unauthorized");
_;
}
function isAuthorized(address src, bytes4 sig) internal view returns (bool) {
if (src == address(this)) {
return true;
} else if (src == owner) {
return true;
} else if (authority == DSAuthority(0)) {
return false;
} else {
return authority.canCall(src, address(this), sig);
}
}
}
contract DSNote {
event LogNote(bytes4 indexed sig, address indexed guy, bytes32 indexed foo, bytes32 indexed bar, uint wad, bytes fax) anonymous;
modifier note() {
bytes32 foo;
bytes32 bar;
uint wad;
assembly {
foo := calldataload(4)
bar := calldataload(36)
wad := callvalue()
}
_;
emit LogNote(msg.sig, msg.sender, foo, bar, wad, msg.data);
}
}
// DSProxy
// Allows code execution using a persistant identity This can be very
// useful to execute a sequence of atomic actions. Since the owner of
// the proxy can be changed, this allows for dynamic ownership models
// i.e. a multisig
contract DSProxy is DSAuth, DSNote {
DSProxyCache public cache; // global cache for contracts
constructor(address _cacheAddr) public {
setCache(_cacheAddr);
}
fallback() external payable {}
// use the proxy to execute calldata _data on contract _code
function execute(
bytes memory _code,
bytes memory _data,
bool bools
) external payable returns (address target, bytes memory response) {
target = cache.read(_code);
if (target == address(0)) {
// deploy contract & store its address in cache
target = cache.write(_code);
}
response = execute(target, _data);
}
function execute(address _target, bytes memory _data) public payable auth note returns (bytes memory response) {
require(_target != address(0), "ds-proxy-target-address-required");
// call contract in current context
assembly {
let succeeded := delegatecall(sub(gas(), 5000), _target, add(_data, 0x20), mload(_data), 0, 0)
let size := returndatasize()
response := mload(0x40)
mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f))))
mstore(response, size)
returndatacopy(add(response, 0x20), 0, size)
switch iszero(succeeded)
case 1 {
// throw if delegatecall failed
revert(add(response, 0x20), size)
}
}
}
//set new cache
function setCache(address _cacheAddr) public auth note returns (bool) {
require(_cacheAddr != address(0), "ds-proxy-cache-address-required");
cache = DSProxyCache(_cacheAddr); // overwrite cache
return true;
}
}
// DSProxyFactory
// This factory deploys new proxy instances through build()
// Deployed proxy addresses are logged
contract DSProxyFactory {
event Created(address indexed sender, address indexed owner, address proxy, address cache);
mapping(address => bool) public isProxy;
DSProxyCache public cache;
constructor() public {
cache = new DSProxyCache();
}
// deploys a new proxy instance
// sets owner of proxy to caller
function build() external returns (address payable proxy) {
proxy = build(msg.sender);
}
// deploys a new proxy instance
// sets custom owner of proxy
function build(address owner) public returns (address payable proxy) {
proxy = address(new DSProxy(address(cache)));
emit Created(msg.sender, owner, address(proxy), address(cache));
DSProxy(proxy).setOwner(owner);
isProxy[proxy] = true;
}
}
// DSProxyCache
// This global cache stores addresses of contracts previously deployed
// by a proxy. This saves gas from repeat deployment of the same
// contracts and eliminates blockchain bloat.
// By default, all proxies deployed from the same factory store
// contracts in the same cache. The cache a proxy instance uses can be
// changed. The cache uses the sha3 hash of a contract's bytecode to
// lookup the address
contract DSProxyCache {
mapping(bytes32 => address) cache;
function read(bytes memory _code) external view returns (address) {
bytes32 hash = keccak256(_code);
return cache[hash];
}
function write(bytes memory _code) external returns (address target) {
assembly {
target := create(0, add(_code, 0x20), mload(_code))
switch iszero(extcodesize(target))
case 1 {
// throw if contract failed to deploy
revert(0, 0)
}
}
bytes32 hash = keccak256(_code);
cache[hash] = target;
}
}
// ProxyRegistry
// This Registry deploys new proxy instances through DSProxyFactory.build(address) and keeps a registry of owner => proxy
contract ProxyRegistry {
mapping(address => DSProxy) public proxies;
DSProxyFactory factory;
constructor(address factory_) public {
factory = DSProxyFactory(factory_);
}
// deploys a new proxy instance
// sets owner of proxy to caller
function build() external returns (address payable proxy) {
proxy = build(msg.sender);
}
// deploys a new proxy instance
// sets custom owner of proxy
function build(address owner) public returns (address payable proxy) {
require(proxies[owner] == DSProxy(0) || proxies[owner].owner() != owner); // Not allow new proxy if the user already has one and remains being the owner
proxy = factory.build(owner);
proxies[owner] = DSProxy(proxy);
}
}
{
"compilationTarget": {
"contracts/deploy/DSProxyContract.sol": "DSProxy"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 20
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_cacheAddr","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":true,"inputs":[{"indexed":true,"internalType":"bytes4","name":"sig","type":"bytes4"},{"indexed":true,"internalType":"address","name":"guy","type":"address"},{"indexed":true,"internalType":"bytes32","name":"foo","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"bar","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"fax","type":"bytes"}],"name":"LogNote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authority","type":"address"}],"name":"LogSetAuthority","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"}],"name":"LogSetOwner","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract DSAuthority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cache","outputs":[{"internalType":"contract DSProxyCache","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_target","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"execute","outputs":[{"internalType":"bytes","name":"response","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_code","type":"bytes"},{"internalType":"bytes","name":"_data","type":"bytes"},{"internalType":"bool","name":"bools","type":"bool"}],"name":"execute","outputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"bytes","name":"response","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract DSAuthority","name":"authority_","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_cacheAddr","type":"address"}],"name":"setCache","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"}]