If I understand your question correctly you want to be able to see who has deposited Eth to your contract address. This is what event logs are for.
(1) Create a contract where there is an event every time there is a transaction. e.g something like:
contract someContract {
address public owner;
// Set the owner of the contract to be the creator of the contract i.e. you
function someContract() {
owner = msg.sender;
}
// This is an event
event DepositMade(address _from, uint value);
event WithdrawalMade(address _to, uint value);
//Catch all function
function() {
// generate an event when someone sends you Eth
if (msg.value > 0)
DepositMade(msg.sender, msg.value);
}
// Only the owner of the site can withdraw Eth
modifier admin { if (msg.sender == owner) _ }
function withdraw(uint amount, address recipient) admin {
if(recipient.send(amount))
WithdrawalMade(msg.sender, msg.value);
else throw;
}
}
The important bits are defining an event type event DepositMade(address _from, uint value)
and generating an event when something happens DepositMade(msg.sender, msg.value);
these events are stored in the event log associated with the address of the deployed contract instance.
(2) You retrieve the events on this contract using rpc eth_newFilter
or web3.eth.filter
e.g something like:
var filter = web3.eth.filter({fromBlock:0, toBlock: 'latest', address: contractAddress, 'topics':['0x' + web3.sha3('DepositMade(hexstring,uint256)')]});
filter.watch(function(error, result) {
if(!error) console.log(result);
})
You're trying to apply a model that is appropriate for Bitcoin mainly due to its limitations. It will be an awkward strategy for Ethereum that overlooks it's strengths.
Bitcoin: Not especially programmable, but easy to sweep funds from multiple accounts.
Ethereum: Especially programmable, but not so easy to sweep funds from multiple accounts.
Ethereum also has unique security considerations and it's important to confirm that your client has all the capabilities your contract assumes they have, such as the ability to form a transaction and sign a withdrawal request. That is NOT the case if they are sending from another exchange, so you need to prevent it. You probably need registration/KYC in any case, so confirm they have full control of the accounts they will use, and then inform the contract they are "approved" to send money.
- You can use a single deposit account - all money together, so no
need to sweep.
- You can know who sent it and emit events, record it
etc. - all accounting requirements.
- It is programmable - whatever happens next.
You should confirm that your users can sign a transaction. This will ensure they can work with any contract interface you show them in the future.
- Have them sign a message and send it to you, off-chain (free). Confirm their address with ecrecover.
- Now you know who is who. Not from where the money arrived. By where it came from. No reason a user can't confirm multiple accounts (step 1) but it is unwise to accept funds from unknown senders.
For brevity, I will skip over withdrawal functionality because you will probably transfer the funds to operating accounts as soon as they are received and you might have a completely different contract where you send funds to be withdrawn - or send to the user based on a web site interaction.
This is not presented as a battle-ready production contract, but more like an simple example to show the approach.
pragma solidity 0.5.16;
contract Deposit {
address payable public wallet;
mapping(address => bool) public approved;
event LogApproved(address approver, address approved);
event LogDeposit(address sender, uint amount);
function authorize(address allowed) public {
require(msg.sender == wallet, "Only the owner can do this, e.g. from their admin server.");
emit LogApproved(msg.sender, allowed);
approved[allowed] = true;
}
function deposit() public payable {
require(approved[msg.sender], "Unauthorized sender. Please register.");
emit LogDeposit(msg.sender, msg.value); // <=== there's your accounting
wallet.transfer(msg.value);
}
}
The accounting concern is immortalized on the blockchain and is inspectable by an external client, e.g. nodejs. The money goes to your wallet
account and you can carry on from there.
Hope it helps.
Best Answer
Here's an intuition. The simplest transaction in Ethereum costs 21000 gas and looks like this:
When you're depositing
to
the exchange, the exchange can watch for transactions that are sentto
this deposit address. For example, for block 99 they could useweb3.eth.getBlock(99, true, ...)
, and for each transaction object check ifto
equals the deposit address, and thevalue
(amount of Ether in wei) that was sent. Relatively straightforward for the exchange to see what you've deposited.But if you are using a contract to make a deposit, the
to
is no longer the deposit address that the exchange gave you. Theto
is the address of your contract, and in the transaction there will be an additionaldata
field. Thisdata
provides instructions to the contract, to execute code, which may then transfer the Ether to the deposit address. The exchange has to also execute the code, if it wants to be sure that it received funds from such execution. (An exchange could use web3.eth.getBalance to monitor balances of deposit addresses, but if it wants to know exactly where the funds came from, it has to execute the code of all transactions.)That's an overview and to read more, the Ethereum Wiki has links to other material such as the Yellow Paper, and White Paper. Reading this StackExchange for highly voted questions and answers, with tags like transactions and gas is also helpful.
Technically, transactions are only initiated by humans: a contract invoking another contract is a
message call
(as defined in the Yellow Paper) not a transaction. Only the transaction is recorded on the blockchain because all further execution can be derived from the transaction.For why withdrawing from an exchange to a contract wallet/account is not straightforward, it's because more gas is needed than the basic 21000 gas. Different contracts could consume different amounts of gas, and sufficient gas must be provided by the exchange otherwise the transaction does not take effect: the funds remain will remain with the exchange.