Here is an answer that I'm completely unsure of, so wait for some responses before you try it...
Implement, compile and deploy the follow contract:
pragma solidity 0.4.24;
contract MyContract {
address public myWallet;
constructor() public {
myWallet = msg.sender;
}
function() public payable {
myWallet.transfer(address(this).balance);
}
}
Then, from the off-chain, call function execute
with the address of this contract and the value that you want to withdraw. Needless to say, all amounts are always in WEI.
Here is a web3.js-v1 example:
let fs = require("fs");
let Web3 = require("web3");
let web3 = new Web3(NODE_ADDRESS);
function deployed(contractName, contractAddr) {
let abi = fs.readFileSync(contractName + ".abi").toString();
return new web3.eth.Contract(JSON.parse(abi), contractAddr);
}
async function send(transaction) {
let block = await web3.eth.getBlock("latest");
let options = {
to : transaction._parent._address,
data: transaction.encodeABI(),
gas : block.gasLimit
};
let signedTransaction = await web3.eth.accounts.signTransaction(options, PRIVATE_KEY);
return await web3.eth.sendSignedTransaction(signedTransaction.rawTransaction);
}
async function withdraw(amount) {
let multiOwned = deployed("MultiOwned", "0x5248e5fd5d3b43126e5352807f75d6a7d6d7eafb");
let myContract = deployed("MyContract", "0xThePublicAddressOfMyContractOnTheMainNet");
let receipt = await send(multiOwned.methods.execute(myContract._address, amount, []));
console.log(JSON.stringify(receipt, null, 4));
}
Like I said, I'm unsure of this answer, so wait for some comments, feedback and suggestions...
UPDATE:
You can withdraw your funds with a few modifications to the code above.
Step #1 - create a file called withdraw.js
with the following contents:
// These parameters serve as an example:
let ETH_NODE_ADDRESS = "http://localhost:8545";
let WITHDRAW_WEI_AMOUNT = "1000000000000000000"; // 1 ether
let WALLET_PUBLIC_ADDR = "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf";
let WALLET_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000001";
let REQUIRED_CONFIRMS = 3;
let SIGNER_PRIVATE_KEYS = [
"0000000000000000000000000000000000000000000000000000000000000002",
"0000000000000000000000000000000000000000000000000000000000000003",
"0000000000000000000000000000000000000000000000000000000000000004",
"0000000000000000000000000000000000000000000000000000000000000005",
];
let MS_CONTRACT_ADDR = "0x5248e5fd5d3b43126e5352807f75d6a7d6d7eafb";
let MS_CONTRACT_ABI = '[{"constant":false,"inputs":[{"name":"_owner","type":"address"}],"name":"removeOwner","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_addr","type":"address"}],"name":"isOwner","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"m_numOwners","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"m_lastDay","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"version","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"resetSpentToday","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"m_spentToday","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_owner","type":"address"}],"name":"addOwner","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"m_required","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"_h","type":"bytes32"}],"name":"confirm","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_newLimit","type":"uint256"}],"name":"setDailyLimit","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"},{"name":"_data","type":"bytes"}],"name":"execute","outputs":[{"name":"_r","type":"bytes32"}],"type":"function"},{"constant":false,"inputs":[{"name":"_operation","type":"bytes32"}],"name":"revoke","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_newRequired","type":"uint256"}],"name":"changeRequirement","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_operation","type":"bytes32"},{"name":"_owner","type":"address"}],"name":"hasConfirmed","outputs":[{"name":"","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"}],"name":"kill","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"}],"name":"changeOwner","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"m_dailyLimit","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"inputs":[{"name":"_owners","type":"address[]"},{"name":"_required","type":"uint256"},{"name":"_daylimit","type":"uint256"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"operation","type":"bytes32"}],"name":"Confirmation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"operation","type":"bytes32"}],"name":"Revoke","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldOwner","type":"address"},{"indexed":false,"name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newOwner","type":"address"}],"name":"OwnerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"oldOwner","type":"address"}],"name":"OwnerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"newRequirement","type":"uint256"}],"name":"RequirementChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"SingleTransact","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"owner","type":"address"},{"indexed":false,"name":"operation","type":"bytes32"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"MultiTransact","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"operation","type":"bytes32"},{"indexed":false,"name":"initiator","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"to","type":"address"},{"indexed":false,"name":"data","type":"bytes"}],"name":"ConfirmationNeeded","type":"event"}]';
let MY_CONTRACT_ABI = '[{"constant":true,"inputs":[],"name":"myWallet","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"}]';
let MY_CONTRACT_BIN = "0x608060405234801561001057600080fd5b5060008054600160a060020a0319163317905561010b806100326000396000f300608060405260043610603e5763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663b880b69a81146088575b6000805460405173ffffffffffffffffffffffffffffffffffffffff90911691303180156108fc02929091818181858888f193505050501580156085573d6000803e3d6000fd5b50005b348015609357600080fd5b50609a60c3565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b60005473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a72305820b8ebc070e327daa9cddc13ee6bd5b4035c70358eaaeaf527728c3c5f5c61806d0029";
let Web3 = require("web3");
let web3 = new Web3(ETH_NODE_ADDRESS);
function deployed(abi, address) {
return new web3.eth.Contract(JSON.parse(abi), address);
}
async function deploy(abi, bin, args, key) {
let contract = new web3.eth.Contract(JSON.parse(abi));
let handle = await send(contract.deploy({data: bin, arguments: args}), key);
return new web3.eth.Contract(JSON.parse(abi), handle.contractAddress);
}
async function send(transaction, key) {
let block = await web3.eth.getBlock("latest");
let options = {
to : transaction._parent._address,
data: transaction.encodeABI(),
gas : block.gasLimit
};
let signedTransaction = await web3.eth.accounts.signTransaction(options, key);
return await web3.eth.sendSignedTransaction(signedTransaction.rawTransaction);
}
async function withdraw() {
let msContract = deployed(MS_CONTRACT_ABI, MS_CONTRACT_ADDR);
let myContract = await deploy(MY_CONTRACT_ABI, MY_CONTRACT_BIN, [], WALLET_PRIVATE_KEY);
let balanceBefore = await web3.eth.getBalance(WALLET_PUBLIC_ADDR);
for (let i = 0; i < REQUIRED_CONFIRMS; i++)
await send(msContract.methods.execute(myContract._address, WITHDRAW_WEI_AMOUNT, "0x00"), SIGNER_PRIVATE_KEYS[i]);
let balanceAfter = await web3.eth.getBalance(WALLET_PUBLIC_ADDR);
console.log("balanceBefore =", balanceBefore);
console.log("balanceAfter =", balanceAfter );
}
withdraw();
Step #2 - set the parameters at the top of this file to whatever you have on your system:
let ETH_NODE_ADDRESS = "http://localhost:8545";
let WITHDRAW_WEI_AMOUNT = "1000000000000000000"; // 1 ether
let WALLET_PUBLIC_ADDR = "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf";
let WALLET_PRIVATE_KEY = "0000000000000000000000000000000000000000000000000000000000000001";
let REQUIRED_CONFIRMS = 3;
let SIGNER_PRIVATE_KEYS = [
"0000000000000000000000000000000000000000000000000000000000000002",
"0000000000000000000000000000000000000000000000000000000000000003",
"0000000000000000000000000000000000000000000000000000000000000004",
"0000000000000000000000000000000000000000000000000000000000000005",
];
Note that you'll be using this wallet (WALLET_PUBLIC_ADDR
and WALLET_PRIVATE_KEY
) for:
- Deploying the
MyContract
contract that I have described at the top of this answer to mainnet
- Withdrawing the funds from the multi-sig wallet that you have previously deployed to mainnet
So be sure to load some funds into this wallet (I'd say 0.2 Ether should do).
Step #3 - download Node.js and npm from https://nodejs.org/en/, and then install them.
Step #4 - open a command line at the same path where you saved the file withdraw.js
, and then:
- Run
npm install web3
- Run
node withdraw.js
UPDATE #2:
A few notes on the solution above:
Be sure to load some funds into each one of the signer-wallets, in particularly the last one (e.g., suppose that you need 3 out of 5 signers, then the 3rd wallet on the list requires a little more than the first two). My script requires only the private keys of the signer-wallets, but you'll need their public addresses as well of course, in order to load funds into them.
Note that I am using the compiled code of both my contract (provided at the top of this answer) and your contract (deployed to mainnet), in order to save you the hustle of doing it yourself (compiling my contract and importing your compiled contract). Nevertheless, you shouldn't trust me with this piece of data, or even suppose someone has hacked this answer and changed it.
So here is how you can do it yourself, in case you decide not to trust me:
let MS_CONTRACT_ADDR = "Paste your contract address here, preceded by 0x"
let MS_CONTRACT_ABI = 'Copy your contract abi from etherscan and paste it here'
let MY_CONTRACT_ABI = 'Compile my contract and paste its abi here'
let MY_CONTRACT_BIN = "Compile my contract and paste its bin here, preceded by 0x"
execute
merely performs dynamic invoking, i.e., it takes byte-code as input (bytes _data
) and executes it. This doesn't have anything to do with withdrawing ether from the contract, and as a matter of fact, since none of the functions in this contract ispayable
, I doubt that there is any ether in it to begin with. Is it possible that the ether funds that you refer to are actually located in a contract owned by thisMultiSigWalllet
contract (whose functions can be executed only by the authorized signers of thisMultiSigWalllet
contract)?