Closed AugustoL closed 2 years ago
@AugustoL the main issue of old ERC827 implementation was that the token may own some other tokens/resources and performing arbitary calls allows someone to steal this resources. I propose to forward calls with another contract, which should own nothing... and nothing could be stolen...
@k06a yes thats one of the problems, but another one is the compatibility with another token standards that assume that no arbitrary calls can be executed from a token contract, like https://github.com/windingtree/erc827/blob/master/contracts/examples/VaultAttack.sol.
But reviewing it again I saw that it may also solve that issue too, which is great. We should consider those changes and compare it to the existent proposals :)
@AugustoL I personally think I could use the proposed version with my current project: MultiToken - ERC20 token abstraction, which contains some nested ERC20 tokens. It was impossible with old version because someone was able to steal upgraded or AirDropped tokens from MultiToken contract.
@k06a why not collaborate here first?
@AugustoL sure! Not going to use it until we found a best possible solution. Working on PR to this repo: https://github.com/windingtree/erc827
@AugustoL Here it is: https://github.com/windingtree/erc827/pull/3
We have a proposal for changes to fix current issues in the token standard and also add more control over which functions can be executed from the token contract allowing the receiver contracts to have control ownership over it.
The receiver contract where the callback function will be executed now needs to allow the execution of that callback itself, if the sender attempt to execute a function that is not allowed on a receiver contract it will fail. The callback can be allowed to certain address or they can be public, which means that anyone can execute it. There is also the option for a receiver contract to allow the execution of any function from any address.
The allowedCallbacks mapping works like this:
Receiver => Sender => Function Signature => FunctionType => Allowed
The receiver
is the contract where the callbak function will be executed.
The sender
can be an specific address or it can be 0x0, if it is 0x0 means that is a public callback.
The function signature
can be an specific signature or it can be 0x0, if it is 0x0 means that any function can be executed.
The function type
can be 1(approval), 2(transfer) or 3(transferFrom).
The allowed
variable is a boolean that is true or false depending if the callback is allowed by the sender or not.
To manage the allowance of the callback the proposal adds two new functions:
function allowCallback(address from, bytes4 functionSignature, uint8 fType) public
function removeCallback(address from, bytes4 functionSignature, uint8 fType) public
Implementation: https://github.com/windingtree/erc827/blob/master/contracts/ERC827/proposals/ERC827TokenMockAllowedCallbacks.sol
Merged pull request with description and discussion: https://github.com/windingtree/erc827/pull/2
Thanks @Perseverance for the help and review :clap:
Second proposal submitted to the windingtree/erc827 repository: https://github.com/windingtree/erc827/pull/4
The proposal adds an ERC827Proxy to the token to be used for every method that will execute arbitrary calls, doing this the msg.sender in the receiver contracts will be the ERC827Proxy address and not the ERC827 token,
The implementation is done in the contracts/proposals folder on the ERC827TokenWithProxy.sol file. The same tests that are being used for ERC827Token are used for this implementation with the same results.
My thoughts: I like a lot this one, it does not give as much control over the arbitrary calls as the AllowedCallbacks but it solves the main issue, making calls in behalf of the token contract.
Based on the proxy idea by @k06a
Implementation of proxy proposal simplified and ready to be merged windingtree/erc827#4
@AugustoL would you mind summarizing what the current state of this proposal. from what I can understand:
On a related note, do you know if the vulnerabilities also apply to #677, or does the fact that it defines a specific callback method (tokenFallback) mean it is not affected?
@ptrwtts approveAndCall
have issues related to arbitrary call attack from token smart contract. Attacker can spend anything this smart contract own. I personally don’t like idea of permissioning some method signatures, I prefer to extract arbitrary call to another smart contract called Caller, which owns nothing. But it seems we can’t reach the consensus in this question, I really wonder why :)
@k06a thanks. why not just use the old school approveAndCall
, as seen many past token implementations (e.g. MiniMe) , or the transferAndCall
from #677? what are the main benefits of being able to call any arbitrary method?
@ptrwtts calling arbitrary method allows you to send your token to any smart contract in a way it supports. There are some DEXes with different smart contract interfaces. No need for all contract to support special token protocol, just being compatible with ERC20. #677 has a problem with checks, anyone implementing receiveApproval
should perform checks, that it was called from token and not from any hacker with random arguments. Please look at #1003, I am suggesting this implementation because it is fully compatible with ERC20, even transferFrom
is called for msg.sender
as in simple ERC20.
TransferAndCall is this function ‘_to’ a contract address or a receiver address?
@RNHaoHao to the contract address, then you should redistribute the approval from the contract.
@AugustoL _to represents the address of other contracts. Functions that can be invoked by other contracts?
"TransferAndCall" I always failed to call ("0xacb6208f4fbc6ac8fb60ad92f7096b4100538f45",100,"0xa9059cbb0000000000000000000000004ffe370c88549717a552f9ed7aac84b12e6bc1680000000000000000000000000000000000000000000000000de0b6b3a7640000")
PR created to integrate proxy proposal into standard and update to solidity v0.5.0 https://github.com/windingtree/erc827/pull/14
PR created to change proxy handling, instead of using one single proxy a proxy can be created and destroyed for each call without any remaining balance of tokens and ETH left in it.
This also allows the verification of the original msg.sender by calculating the address of the proxy in the receiver contract in case if needed.
https://github.com/windingtree/erc827/pull/18
How approveAndCall would work: 1.- Sender calls the Token to approve 100 tokens to the Receiver contract and execute claimTokens function in the Receiver contract. 2.- The Token contract changes the allowance of Sender to Receiver to 100, creates a ERC827Proxy contract where the salt is keccak256(sender, receiver, nonces[sender]). Important note: The token has two public variables, one is proxyBytecode and the other is a mapping of the nonces by address. 3.- The Token contract executes the call to the receiver through the proxy. 4.- The Receiver contract can access the allowed balance and also verify that the sender address by comparing the msg.sender (which would be the proxy address) to the address computed by create2 of the proxy, having everything it needs to compute it (sender, token address, proxyBytecode and nonce of the sender accesible as public variables on the token).
This is definitely much better than a single proxy since it allows the verification of the original sender if necessary.
This is definitely much better than a single proxy since it allows the verification of the original sender if necessary.
And because the proxy address is not being reused and shared between accounts now!
@AugustoL what is the status of ERC827, as of your latest proposal to dynamically create and destroy poxies (https://github.com/ethereum/EIPs/issues/827#issuecomment-501310739)?
Is ERC827 in use in the real world? I am trying to decide whether to add this interface to a contract, and I'm interested to know if this has been battle-tested, and/or if it has seen any adoption.
Also, what does it mean that "This standard... is proven to be unsafe to be used" mean? What part is unsafe, specifically?
I am going to close this as GitHub is no longer the proper place to discuss ideas for EIPs (it used to be). If the author of this wants to pursue this further, they can either create a thread on Ethereum Magicians to continue discussion, or they can create a draft EIP and start working through the EIP process as described in EIP-1.
@AugustoL
This standard is still a draft and is proven to be unsafe to be used
Simple Summary
A extension of the standard interface ERC20 for tokens with methods that allows the execution of calls inside transfer and approvals.
Abstract
This standard provides basic functionality to transfer tokens, as well as allow tokens to be approved so they can be spent by another on-chain third party. Also it allows to execute calls on transfers and approvals.
Motivation
This extension of the ERC20 interface allows the token to execute a function in the receiver contract contract after the approval or transfer happens. The function is executed by the token proxy, a simple proxy which goal is to mask the msg.sender to prevent the token contract to execute the function calls itself. The ERC20 token standard is widely accepted but it only allows the transfer of value, ethereum users are available to transfer value and data on transactions, with these extension of the ERC20 token standard they will be able to do the same with ERC20 tokens.
I saw a lot of new standards being proposed in the community and I think the way to improve the current ERC20 standard is with an extension that is fully compatible with the original standard and also add new methods, but keeping it simple at the same time, the code to be added to the ERC20 standard is near 150 lines of code.
When to use each function
approveAndCall: Probably the one that you will need, maybe the only one since it only allows the receiver contract to use approved balance. The best practice is to check the allowance of the sender and then do your stuff using the transferFromAndCall method.
transferAndCall: There is no way to check that the balance that will be transferred is the correct one, this function is useful when a function dont need to check any transfer of value.
transferFromAndCall: Same as transferAndCall, only useful when there is no need to check the transfered amount of tokens and want to spend approved balance.
Specification
Token
Methods
NOTE: Callers MUST handle
false
fromreturns (bool success)
. Callers MUST NOT assume thatfalse
is never returned!name - ERC20
Returns the name of the token - e.g.
"MyToken"
.OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present.
symbol - ERC20
Returns the symbol of the token. E.g. "HIX".
OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present.
decimals - ERC20
Returns the number of decimals the token uses - e.g.
8
, means to divide the token amount by100000000
to get its user representation.OPTIONAL - This method can be used to improve usability, but interfaces and other contracts MUST NOT expect these values to be present.
totalSupply - ERC20
Returns the total token supply.
balanceOf - ERC20
Returns the account balance of another account with address
_owner
.transfer - ERC20
Transfers
_value
amount of tokens to address_to
, and MUST fire theTransfer
event. The function SHOULDrevert
if the_from
account balance does not have enough tokens to spend.A token contract which creates new tokens SHOULD trigger a Transfer event with the
_from
address set to0x0
when tokens are created.Note Transfers of 0 values MUST be treated as normal transfers and fire the
Transfer
event.transferFrom - ERC20
Transfers
_value
amount of tokens from address_from
to address_to
, and MUST fire theTransfer
event.The
transferFrom
method is used for a withdraw workflow, allowing contracts to transfer tokens on your behalf. This can be used for example to allow a contract to transfer tokens on your behalf and/or to charge fees in sub-currencies. The function SHOULDrevert
unless the_from
account has deliberately authorized the sender of the message via some mechanism.Note Transfers of 0 values MUST be treated as normal transfers and fire the
Transfer
event.approve - ERC20
Allows
_spender
to withdraw from your account multiple times, up to the_value
amount. If this function is called again it overwrites the current allowance with_value
.Users SHOULD make sure to create user interfaces in such a way that they set the allowance first to
0
before setting it to another value for the same spender. THOUGH The contract itself shouldn't enforce it, to allow backwards compatibility with contracts deployed beforeallowance - ERC20
Returns the amount which
_spender
is still allowed to withdraw from_owner
.ERC827 Proxy
A very simple proxy contract used to forward the calls form the token contract.
There is a public variable called proxy in the ERC827 token, this can be used to check if the call is coming from the ERC827 token since the proxy can only forward calls from the token contract.
ERC827 methods
transferAndCall - ERC827
Execute a function on
_to
with the_data
parameter, if the function ends successfully execute the transfer of_value
amount of tokens to address_to
, and MUST fire theTransfer
event.This method is
payable
, which means that ethers can be sent when calling it, but the transfer of ether needs to be handled in the call is executed after transfer since the one who receives the ether is the token contract and not the token receiver.The function SHOULD
revert
if the call to_to
address fails or if_from
account balance does not have enough tokens to spend. The ERC20transfer
method is called before the_call(_to, _data)
.Note Transfers of 0 values MUST be treated as normal transfers and fire the
Transfer
event.Important Note Do not use this method with fallback functions that receive the value transferred as parameter, there is not way to verify how much value was transferred on the fallback function.
transferFromAndCall - ERC827
Execute a function on
_to
with the_data
parameter, if the function ends successfully execute the transfer of_value
amount of tokens from address_from
to address_to
, and MUST fire theTransfer
event.This method is
payable
, which means that ethers can be sent when calling it, but the transfer of ether needs to be handled in the call is executed after transfer since the one who receives the ether is the token contract and not the token receiver.The
transferFromAndCall
method is used for a withdraw workflow, allowing contracts to transfer tokens on your behalf before executing a function. The ERC20transferFrom
method is called before the_call(_to, _data)
. This can be used for example to allow a contract to transfer tokens on your behalf and/or to charge fees in sub-currencies. The function SHOULDrevert
if the call to_to
address fails or if the_from
approved balance by_from
tomsg.sender
is not enough to execute the transfer.Note Transfers of 0 values MUST be treated as normal transfers and fire the
Transfer
event.Important Note Do not use this method with fallback functions that receive the value transferred as parameter, there is not way to verify how much value was transferred on the fallback function.
approveAndCall - ERC827
Execute a function on
_spender
with the_data
parameter, if the function ends successfully allows_spender
to withdraw from your account multiple times, up to the_value
amount. If this function is called again it overwrites the current allowance with_value
.This method is
payable
, which means that ethers can be sent when calling it, but the transfer of ether needs to be handled in the call is executed after transfer since the one who receives the ether is the token contract and not the token receiver.Clients SHOULD make sure to create user interfaces in such a way that they set the allowance first to
0
before setting it to another value for the same spender. The ERC20approve
method is called before the_call(_spender, _data)
. The function SHOULDrevert
if the call to_spender
address fails. THOUGH The contract itself shouldn't enforce it, to allow backwards compatibility with contracts deployed beforeEvents
Transfer - ERC20
MUST trigger when tokens are transferred, including zero value transfers.
Approval - ERC20
MUST trigger on any successful call to
approve(address _spender, uint256 _value)
.Past Issues
The main issue that has been recognized by the community is that the standard does not follow the assumption about executing calls in behalf of a token contract, every smart contract that handle token balances assume the token contract will execute only the common methods and maybe a callback that is implemented by the token itself. This standard break that rule and allow the execution of arbitrary calls making it hard to integrate in current solutions. UPDATE This was solved by adding a simple proxy to the token and forwarding the calls coming from the token contract, the proxy ensure that the calls come only from the token contract and allows this to be verified on chain, this prevents the token address to be used as
msg.sender
allowing the integration with current solutions.Discussion channel
https://gitter.im/ERC827
Revisions
Implementation
ERC827 Interface in Winding Tree
[ERC827 Standard Token implementation in Winding Tree](https://github.com/windingtree/erc827/blob/master/contracts/ERC827/ERC827.sol
Copyright
Copyright and related rights waived via CC0