Created
June 21, 2020 13:57
-
-
Save drortirosh/a1d984208f215638ed87436bdb47892b to your computer and use it in GitHub Desktop.
Sample on funding proxy account without eth.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//SPDX-License-Identifier: MIT | |
pragma solidity ^0.6.9; | |
//helper: decode error message | |
// (NOT efficient: using calldata, which means string copies..) | |
library LibError { | |
function getError(string calldata txt, bytes calldata ret) external pure returns(string memory) { | |
if ( ret.length<36) return ""; | |
return string(abi.encodePacked(txt, abi.decode(ret[4:], (string)))); | |
} | |
} | |
//sample funder contract. can only fund its owner. | |
contract Funder { | |
address owner; | |
constructor(address _owner) public payable { | |
owner=_owner; | |
} | |
receive() external payable {} | |
//owner ask to get funded. | |
// called from a proxy object | |
// (real funder will have approval to extract tokens from owner.) | |
function fundMe(uint value) external { | |
require( msg.sender == owner, "funder: not owner"); | |
msg.sender.transfer(value); | |
} | |
//Helper method, to be called by Proxy, with "delegatecall" | |
// (has to be somewhere, not neccesarily inside the funder) | |
// performs a batch of 2 operations: | |
// 1. proxy calls loaner.fundMe | |
// 2. proxy calls target method | |
function execWithFunding(Funder funder, uint value, address target, bytes memory func) public { | |
require( value>0, "fund without value"); | |
bool success; bytes memory ret; | |
//we're in delegatecall: actual sender is the proxy: | |
funder.fundMe(value); | |
// (success, ret) = address(loaner).call(abi.encodeWithSelector(Loaner.fundMe.selector, value)); | |
// require(success, string(abi.encodePacked("funding failed: ", LibError.getError(ret)))); | |
(success, ret) = target.call{value:value}(func); | |
require(success, LibError.getError("exec with fundiing failed: ",ret)); | |
} | |
} | |
contract Proxy { | |
address owner; | |
constructor(address _owner) public { | |
owner=_owner; | |
} | |
receive() external payable {} | |
function execute(bool delegate, address target, uint value, bytes calldata func) external { | |
require( msg.sender==owner, "exec: not owner"); | |
require( address(this).balance >= value, "exec: not enough balance"); | |
bool success; bytes memory ret; | |
if ( !delegate ) { | |
(success, ret)=target.call{value:value}(func); | |
} else { | |
(success, ret)=target.delegatecall(func); | |
} | |
if ( !success) { | |
require(success, LibError.getError("exec failed: ",ret)); | |
} | |
} | |
} | |
contract Target { | |
event CalledFrom(address sender, uint value); | |
function mustHaveValue() external payable { | |
require( msg.value!= 0 , "mustHaveValue: requires value"); | |
emit CalledFrom(msg.sender, msg.value); | |
} | |
receive() external payable{} | |
} | |
contract ATest { | |
address target; | |
Proxy proxy; | |
address payable funder; | |
constructor() public payable { | |
//NOTE: Remix bug: sometimes it ignores the value passed to constructor... | |
require( msg.value>0, " === test must be given some eth value"); | |
uint v=msg.value; | |
proxy = new Proxy(address(this)); | |
target = address(new Target()); | |
funder = address(new Funder(address(proxy))); | |
funder.transfer(v); | |
//this fails on "not enough balance" (proxy doesn't have eth) | |
// proxy.execute(false, target, v, abi.encodeWithSelector(Target.mustHaveValue.selector)); | |
//this fails on "something: requires value" | |
// proxy.execute(false, target, 0, abi.encodeWithSelector(Target.mustHaveValue.selector)); | |
//this funds the proxy with value and call mustHaveValue() with that value | |
proxy.execute(true, funder, 0, | |
abi.encodeWithSelector(Funder.execWithFunding.selector, | |
funder, v, | |
target, abi.encodeWithSelector(Target.mustHaveValue.selector) | |
)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment