Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update #8

Merged
merged 4 commits into from
May 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions EIPS/eip-1155.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ interface ERC1155TokenReceiver {

/**
@notice Indicates whether a contract implements the `ERC1155TokenReceiver` functions and so can accept ERC1155 token types.
@dev This function MUST return `bytes4(keccak256("isERC1155TokenReceiver()"))` (i.e. 0x0d912442).
@dev This function MUST return `bytes4(keccak256("isERC1155TokenReceiver()"))` (i.e. 0x0d912442).
This function MUST NOT consume more than 5,000 gas.
@return `bytes4(keccak256("isERC1155TokenReceiver()"))`
*/
function isERC1155TokenReceiver() external pure returns (bytes4);
Expand Down Expand Up @@ -251,7 +252,7 @@ To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST op
* MUST revert if length of `_ids` is not the same as length of `_values`.
* MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient.
* MUST revert on any other error.
* After the above conditions are met, this function MUST check if `_to` is a smart contract (eg. code size > 0). If so, it MUST call `onERC1155Received` or `onERC1155BatchReceived` as on `_to` and act appropriately (see "`onERC1155Received` and onERC1155BatchReceived rules" section).
* After the above conditions are met, this function MUST check if `_to` is a smart contract (eg. code size > 0). If so, it MUST call `onERC1155Received` or `onERC1155BatchReceived` on `_to` and act appropriately (see "`onERC1155Received` and onERC1155BatchReceived rules" section).
* MUST emit `TransferSingle` or `TransferBatch` event(s) on transfer success (see "TransferSingle and TransferBatch event rules" section).
* Transfers and events MUST occur in the array order they were submitted (_ids[0]/_values[0] before _ids[1]/_values[1], etc).

Expand Down Expand Up @@ -317,7 +318,9 @@ To be more explicit about how safeTransferFrom and safeBatchTransferFrom MUST op
return 0x0d912442; // bytes4(keccak256("isERC1155TokenReceiver()"))
}
```
* The implementation MAY differ from the above but it MUST return the same value.
* The implementation MAY differ from the above but:
- It MUST return the same value.
- It MUST NOT consume more than 5,000 gas.

**_Implementation specific transfer api rules:_**
* If implementation specific api functions are used to transfer 1155 tokens to a contract, the safeTransferFrom, or safeBatchTransferFrom (as appropriate) rules MUST be followed.
Expand All @@ -333,7 +336,7 @@ To cater for this scenario, there is some leeway with the rejection logic should

Hence in a hybrid 1155 contract implementation an extra call MUST be made on the recipient contract and checked before any hook calls to `onERC1155Received` or `onERC1155BatchReceived` are made.
Order of operation MUST therefore be:
1. The implementation MUST call the function `isERC1155TokenReceiver` on the recipient.
1. The implementation MUST call the function `isERC1155TokenReceiver` on the recipient, providing at least 5,000 gas.
2. If the function call succeeds and the return value is `bytes4(keccak256("isERC1155TokenReceiver()"))` the implementation proceeds as a regular 1155 implementation, with the call(s) to the `onERC1155Received` or `onERC1155BatchReceived` hooks and rules associated.
3. If the function call fails or the return value is NOT `bytes4(keccak256("isERC1155TokenReceiver()"))` the implementation can assume the contract recipient is not an `ERC1155TokenReceiver` and follow its other standard's rules for transfers.

Expand Down
120 changes: 120 additions & 0 deletions EIPS/eip-1930.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
---
eip: 1930
title: CALLs with strict gas semantic. Revert if not enough gas available.
author: Ronan Sandford (@wighawag)
type: Standards Track
discussions-to: https://github.com/ethereum/EIPs/issues/1930
category: Core
status: Draft
created: 2019-04-10
---

## Simple Summary

Add the ability for smart contract to execute calls with a specific amount of gas. If this is not possible the execution should revert.

## Abstract

The current CALL, DELEGATE_CALL, STATIC_CALL opcode do not enforce the gas being sent, they simply consider the gas value as a maximum. This pose serious problem for applications that require the call to be executed with a precise amount of gas.

This is for example the case for meta-transaction where the contract needs to ensure the call is executed exactly as the signing user intended.

But this is also the case for common use cases, like checking "on-chain" if a smart contract support a specific interface (via [EIP-165](http://eips.ethereum.org/EIPS/eip-165) for example).

The solution presented here is to add new opcodes that enforce the amount of gas specified : the call either proceed with the exact amount of gas or do not get executed and the current call revert.


### Specification

- add a new variant of the CALL opcode where the gas specified is enforced so that if the gas left at the point of call is not enough to give the specified gas to the destination, the current call revert
- add a new variant of the DELEGATE_CALL opcode where the gas specified is enforced so that if the gas left at the point of call is not enough to give the specified gas to the destination, the current call revert
- add a new variant of the STATIC_CALL opcode where the gas specified is enforced so that if the gas left at the point of call is not enough to give the specified gas to the destination, the current call revert

In other words, based on [EIP-150](http://eips.ethereum.org/EIPS/eip-150), the current call must revert unless G >= I x 64/63 where G is gas left at the point of call (after deducing the cost of the call itself) and I is the gas specified.

So instead of
```
availableGas = availableGas - base
gas := availableGas - availableGas/64
...
if !callCost.IsUint64() || gas < callCost.Uint64() {
return gas, nil
}
```
see https://github.com/ethereum/go-ethereum/blob/7504dbd6eb3f62371f86b06b03ffd665690951f2/core/vm/gas.go#L41-L48

we would have
```
availableGas = availableGas - base
gas := availableGas - availableGas/64
if !callCost.IsUint64() || gas < callCost.Uint64() {
return 0, errNotEnoughGas
}
```

### Rationale

Currenlty the gas specified as part of these opcodes is simply a maximum value. And due to the behavior of [EIP-150](http://eips.ethereum.org/EIPS/eip-150) it is possible for an external call to be given less gas than intended (less than the gas specified as part of the CALL) while the rest of the current call is given enough to continue and succeed. Indeed since with EIP-150, the external call is given at max ```G - Math.floor(G/64)``` where G is the gasleft() at the point of the CALL, the rest of the current call is given ```Math.floor(G/64)``` which can be plenty enough for the transaction to succeed. For example, when G = 6,400,000 the rest of the transaction will be given 100,000 gas plenty enough in many case to succeed.

This is an issue for contracts that require external call to only fails if they would fails with enough gas. This requirement is present in smart contract wallet and meta transaction in general, where the one executing the transaction is not the signer of the execution data. Because in such case, the contract needs to ensure the call is executed exactly as the signing user intended.

But this is also true for simple use case, like checking if a contract implement an interface via EIP-165. Indeed as specified by such EIP, the ```supporstInterface``` method is bounded to use 30,000 gas so that it is theorically possible to ensure that the throw is not a result of a lack of gas. Unfortunately due to how the different CALL opcodes behave contracts can't simply rely on the gas value specified. They have to ensure by other means that there is enough gas for the call.

Indeed, if the caller do not ensure that 30,000 gas or more is provided to the callee, the callee might throw because of a lack of gas (and not because it does not support the interace), and the parent call will be given up to 476 gas to continue. This would result in the caller interepreting wrongly that the callee is not implementing the interface in question.

While such requirement can be enforced by checking the gas left according to EIP-150 and the precise gas required before the call (see solution presented in that [bug report](https://web.solidified.io/contract/5b4769b1e6c0d80014f3ea4e/bug/5c83d86ac2dd6600116381f9) or after the call (see the native meta transaction implementation [here](https://github.com/pixowl/thesandbox-contracts/blob/623f4d4ca10644dcee145bcbd9296579a1543d3d/src/Sand/erc20/ERC20MetaTxExtension.sol#L176), it would be much better if the EVM allowed us to strictly specify how much gas is to be given to the CALL so contract implementations do not need to follow [EIP-150](http://eips.ethereum.org/EIPS/eip-150) behavior and the current gas pricing so closely.

This would also allow the behaviour of [EIP-150](http://eips.ethereum.org/EIPS/eip-150) to be changed without having to affect contract that require this strict gas behaviour.

As mentioned, such strict gas behaviour is important for smart contract wallet and meta transaction in general.
The issue is actually already a problem in the wild as can be seen in the case of Gnosis safe which did not consider the behavior of EIP-150 and thus fails to check the gas properly, requiring the safe owners to add otherwise unecessary extra gas to their signed message to avoid the possibility of losing funds. See https://github.com/gnosis/safe-contracts/issues/100

As for EIP-165, the issue already exists in the example implementation presented in the EIP. Please see the details of the issue [here](https://github.com/ethereum/EIPs/pull/881#issuecomment-491677748)

The same issue exists also on OpenZeppelin implementation, a library used by many. It does not for perform any check on gas before calling ```supportsInterface``` with 30,000 gas (see [here](https://github.com/OpenZeppelin/openzeppelin-solidity/blob/fa004a7f5de572b3dbcde1a8a81f9a87e353e799/contracts/introspection/ERC165Checker.sol#L37) and is thus vulnerable to the issue mentioned.


While such issue can be prevented today by checking the gas with EIP-150 in mind, a solution at the opcode level is more elegant.

Indeed, the two possible ways to currently enforce that the correct amount of gas is sent are as follow :

1) check done before the call

```
uint256 gasAvailable = gasleft() - E;
require(gasAvailable - gasAvailable / 64 >= `txGas`, "not enough gas provided")
to.call.gas(txGas)(data); // CALL
```
where E is the gas required for the operation beteen the call to ```gasleft()``` and the actual call PLUS the gas cost of the call itself.
While it is possible to simply over estimate ```E``` to prevent call to be executed if not enough gas is provided to the current call it would be better to have the EVM do the precise work itself. As gas pricing continue to evolve, this is important to have a mechanism to ensure a specific amount of gas is passed to the call so such mechanism can be used without having to relies on a specific gas pricing.


2) check done after the call:

```
to.call.gas(txGas)(data); // CALL
require(gasleft() > txGas / 63, "not enough gas left");
```
This solution does not require to compute a ```E``` value and thus do not relies on a specific gas pricing (except for the behaviour of EIP-150) since if the call is given not enough gas and fails for that reason, the condition above will always fail, ensuring the current call will revert.
But this check still pass if the gas given was less AND the external call reverted or succeeded EARLY (so that the gas left after the call > txGas / 63).
This can be an issue if the code executed as part of the CALL is reverting as a result of a check against the gas provided. Like a meta transaction in a meta transaction.

Similarly to the the previous solution, an EVM mechanism would be much better.

## Backwards Compatibility

Backwards compatible as it introduce new opcodes.

## Test Cases

## Implementation

None fully implemented yet. But see Specifications for an example in geth.

## References

1. EIP-150, http://eips.ethereum.org/EIPS/eip-150

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
81 changes: 81 additions & 0 deletions EIPS/eip-2015.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
eip: 2015
title: Wallet Update Chain JSON-RPC Method (`wallet_updateChain`)
author: Pedro Gomes (@pedrouid)
discussions-to: https://ethereum-magicians.org/t/eip-2015-wallet-update-chain-json-rpc-method-wallet-updatechain/3274
status: Draft
type: Standards Track
category: ERC
created: 2019-05-12
requires: 155, 1474
---

## Simple Summary
Wallets can update the active chain when connected to a Dapp but not vice-versa, with `wallet_updateChain` the Dapp will be able to request this change from the Wallet.

## Abstract
Dapp can request the Wallet to switch chains by providing the minimal parameters of `chainId`, `networkId`, `rpcUrl` and `nativeCurrency`. The Wallet will display a UI element to inform the user of this change.

## Motivation
Wallet and Dapp communication rely on the present provider that acts as middleware between the two. Using JSON-RPC methods, the Dapp is able to access not only the active accounts but also the active chain. With [EIP-1102](https://eips.ethereum.org/EIPS/eip-1102) we introduced the ability for Dapps to request access to the active accounts and the Wallet is able to provide a simple UI to inform the user of this action however the same is not currently possible for switching chains. The current pattern is to display some UI to request the user to switch chains within the Dapp, however this could be easily improved by triggering a UI from the Wallet side that can be approved or rejected by the user instead.

## Specification
The JSON RPC method will be part of `wallet_` namespaced methods which aim to improve the UX and interoperability between Dapps and Wallets.

### Required Parameters
- chainId (number): the id of the chain complaint with EIP-155
- networkId (number): the id of the chain's network
- rpcUrl (string): the url endpoint for RPC requests for this chain
- nativeCurrency (Object): includes two fields for `name` (string) and `symbol` (string)


### Best Practices
- The Wallet should display a UI view similar to a [EIP-1102](https://eips.ethereum.org/EIPS/eip-1102) informing the user that the currently connected Dapp wants to switch to the specified chain.
- the Wallet should default the rpcUrl to any existing endpoints matching a chainId known previously to the wallet, otherwise it will use the provided rpcUrl as a fallback.
- the Wallet should call the rpcUrl with `net_version` and `eth_chainId` to verify the provided chainId and networkId match the responses from the rpcUrl
- the Wallet should change all nativeCurrency symbols to the provided parameter

### Example 1
A JSON-RPC request from a Dapp to switch the Ethereum Goerli chain would be as follows:
```json
{
"id":1,
"jsonrpc": "2.0",
"method": "wallet_updateChain",
"params": [
{
"chainId": 5,
"networkId": 5,
"rpcUrl": "https://goerli.infura.io/v3/406405f9c65348f99d0d5c27104b2213",
"nativeCurrency": {
"name": "Goerli ETH",
"symbol": "gorETH"
}
}
]
}
```

### Example 2
A JSON-RPC request from a Dapp to switch the POA Network's xDAI chain would be as follows:
```json
{
"id":1,
"jsonrpc": "2.0",
"method": "wallet_updateChain",
"params": [
{
"chainId": 100,
"networkId": 100,
"rpcUrl": "https://dai.poa.network",
"nativeCurrency": {
"name": "xDAI",
"symbol": "xDAI"
}
}
]
}
```

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).