diff --git a/docs/ERC-725.md b/docs/ERC-725.md index a09dacee..6fbf383f 100644 --- a/docs/ERC-725.md +++ b/docs/ERC-725.md @@ -17,17 +17,17 @@ The following describes two standards that allow for a generic data storage in a ## Motivation -The initial motivation came out of the need to create a smart contract account system thats flexible enough to be viable long term, but also defined enough to be standardisable, but are in its current form a generic set of standardised modules to be used in all forms of smart contracts. +The initial motivation came out of the need to create a smart contract account system thats flexible enough to be viable long term, but also defined enough to be standardised. They are a generic set of two standardised building blocks to be used in all forms of smart contracts. -This standard consists of two sub standards, a generic data key/value store (ERC725Y) and a generic execute function (ERC725X). Both of which in combination allow for a very flexible and long lasting account system. The account verison of ERC725 is standardised under [LSP0 ERC725Account](https://github.com/lukso-network/LIPs/blob/master/LSPs/LSP-0-ERC725Account.md). +This standard consists of two sub standards, a generic data key/value store (ERC725Y) and a generic execute function (ERC725X). Both of which in combination allow for a very flexible and long lasting account system. The account version of ERC725 is standardised under [LSP0 ERC725Account](https://github.com/lukso-network/LIPs/blob/master/LSPs/LSP-0-ERC725Account.md). -These standareds (ERC725 X and Y) can also be used seperately to enhance NFTs and Token meta data, or allow generic exection for all kinds of smart contracts. +These standareds (ERC725 X and Y) can also be used seperately to enhance NFTs and Token meta data or other types of smart contracts. ERC725X allows for a generic exection through a smart contract, functioning as an account or actor. ## Specification ### Ownership -This contract is controlled by an owner. The owner can be a smart contract or an external account. +This contract is controlled by a single owner. The owner can be a smart contract or an external account. This standard requires [ERC173](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-173.md) and SHOULD implement the functions: - `owner() view` @@ -37,33 +37,28 @@ And the event: - `OwnershipTransferred(address indexed previousOwner, address indexed newOwner)` +--- + ### ERC725X -ERC165 identifier: `0x570ef073` +**ERC725X** interface id according to [ERC165]: `0x570ef073`. -**Note:** The `execute()` function use function overloading, therefore it is better to reference them by the given function signature as follows: +Smart contracts implementing the ERC725X standard MUST implement the [ERC165] `supportsInterface(..)` function and MUST support the ERC165 and ERC725X interface ids. -```js -// web3.js example -myContract.methods['execute(uint256,address,uint256,bytes)'](OPERATION_CALL, target.address, 2WEI, "0x").send(); +### Methods -myContract.methods['execute(uint256[],address[],uint256[],bytes[])']([OPERATION_CALL, OPERATION_CREATE], [target.address, ZERO_ADDRESS], [2WEI, 0WEI], ["0x", CONTRACT_BYTECODE]).send() - -// or -myContract.methods['0x44c028fe'](OPERATION_CALL, target.address, 2WEI, "0x").send(); -``` +Smart contracts implementing the ERC725X standard SHOULD implement all of the functions listed below: #### execute ```solidity -function execute(uint256 operationType, address target, uint256 value, bytes memory data) public payable returns(bytes memory) +function execute(uint256 operationType, address target, uint256 value, bytes memory data) external payable returns(bytes memory) ``` Function Selector: `0x44c028fe` -Executes a call on any other smart contracts, transfers the blockchain native token, or deploys a new smart contract. -MUST only be called by the current owner of the contract. -MUST revert when the execution fails. +Executes a call on any other smart contracts or address, transfers the blockchains native token, or deploys a new smart contract. + _Parameters:_ @@ -72,25 +67,20 @@ _Parameters:_ - `value`: the amount of native tokens to transfer (in Wei). - `data`: the call data, or the creation bytecode of the contract to deploy. -#### data parameter - -- For operationType, `CALL`, `STATICCALL` and `DELEGATECALL` the data field can be random bytes or an abi-encoded function call. - -- For operationType, `CREATE` the `data` field is the creation bytecode of the contract to deploy appended with the constructor argument(s) abi-encoded. -- For operationType, `CREATE2` the `data` field is the creation bytecode of the contract to deploy appended with: - 1. the constructor argument(s) abi-encoded - 2. a bytes32 salt. +_Requirements:_ -``` -data = + + -``` +- MUST only be called by the current owner of the contract. +- MUST revert when the execution or the contract creation fails. +- `target` SHOULD be address(0) in case of contract creation with `CREATE` and `CREATE2` (operation types 1 and 2). +- `value` SHOULD be zero in case of `STATICCALL` or `DELEGATECALL` (operation types 3 and 4). -> Check [EIP-1014: Skinny CREATE2](https://eips.ethereum.org/EIPS/eip-1014) for more information. _Returns:_ `bytes` , the returned data of the called function, or the address of the contract deployed (operation types 1 and 2). -The following `operationType` MUST exist: +**Triggers Event:** [ContractCreated](#contractcreated), [Executed](#executed) + +The following `operationType` COULD exist: - `0` for `CALL` - `1` for `CREATE` @@ -100,14 +90,26 @@ The following `operationType` MUST exist: Others may be added in the future. -**Triggers Event:** [ContractCreated](#contractcreated), [Executed](#executed) +#### data parameter + +- For operationType, `CALL`, `STATICCALL` and `DELEGATECALL` the data field can be random bytes or an abi-encoded function call. + +- For operationType, `CREATE` the `data` field is the creation bytecode of the contract to deploy appended with the constructor argument(s) abi-encoded. + +- For operationType, `CREATE2` the `data` field is the creation bytecode of the contract to deploy appended with: + 1. the constructor argument(s) abi-encoded + 2. a `bytes32` salt. + +``` +data = + + +``` -> **Note:** the operation types `staticcall` (`3`) and `delegatecall` (`4`) do not allow to transfer value. +> See [EIP-1014: Skinny CREATE2](https://eips.ethereum.org/EIPS/eip-1014) for more information. #### execute (Array) ```solidity -function execute(uint256[] memory operationsType, address[] memory targets, uint256[] memory values, bytes[] memory datas) public payable returns(bytes[] memory) +function execute(uint256[] memory operationsType, address[] memory targets, uint256[] memory values, bytes[] memory datas) external payable returns(bytes[] memory) ``` Function Selector: `0x13ced88d` @@ -123,16 +125,45 @@ _Parameters:_ - `values`: the list of native token amounts to transfer (in Wei). - `datas`: the list of call data, or the creation bytecode of the contract to deploy. +_Requirements:_ + +- Parameters array MUST have the same length. +- MUST only be called by the current owner of the contract. +- MUST revert when the execution or the contract creation fails. +- `target` SHOULD be address(0) in case of contract creation with `CREATE` and `CREATE2` (operation types 1 and 2). +- `value` SHOULD be zero in case of `STATICCALL` or `DELEGATECALL` (operation types 3 and 4). + _Returns:_ `bytes[]` , array list of returned data of the called function, or the address(es) of the contract deployed (operation types 1 and 2). **Triggers Event:** [ContractCreated](#contractcreated), [Executed](#executed) on each call iteration + +**Note:** The `execute()` functions use function overloading, therefore it is better to reference them by the given function signature as follows: + +```js +// web3.js example + +// execute +myContract.methods['execute(uint256,address,uint256,bytes)'](OPERATION_CALL, target.address, 2WEI, "0x").send(); + +// execute Array +myContract.methods['execute(uint256[],address[],uint256[],bytes[])']([OPERATION_CALL, OPERATION_CREATE], [target.address, ZERO_ADDRESS], [2WEI, 0WEI], ["0x", CONTRACT_BYTECODE]).send() + +// OR + +// execute +myContract.methods['0x44c028fe'](OPERATION_CALL, target.address, 2WEI, "0x").send(); + +// execute Array +myContract.methods['0x13ced88d']([OPERATION_CALL, OPERATION_CREATE], [target.address, ZERO_ADDRESS], [2WEI, 0WEI], ["0x", CONTRACT_BYTECODE]).send() +``` + ### Events #### Executed ```solidity -event Executed(uint256 indexed operationType, address indexed to, uint256 indexed _value, bytes4 data); +event Executed(uint256 indexed operationType, address indexed target, uint256 indexed value, bytes4 data); ``` MUST be triggered when `execute` creates a new call using the `operationType` `0`, `3`, `4`. @@ -140,27 +171,27 @@ MUST be triggered when `execute` creates a new call using the `operationType` `0 #### ContractCreated ```solidity -event ContractCreated(uint256 indexed operationType, address indexed contractAddress, uint256 indexed value); +event ContractCreated(uint256 indexed operationType, address indexed contractAddress, uint256 indexed value, bytes32 salt); ``` MUST be triggered when `execute` creates a new contract using the `operationType` `1`, `2`. +--- + ### ERC725Y -ERC165 identifier: `0x714df77c` +**ERC725Y** interface id according to [ERC165]: `0x714df77c`. -**Note:** `setData()` and `getData()` uses function overloading, therefore it is better to reference them by the given function signature as follows: +Smart contracts implementing the ERC725Y standard MUST implement the [ERC165] `supportsInterface(..)` function and MUST support the ERC165 and ERC725Y interface ids. -```js -myContract.methods['setData(bytes32[],bytes[])']([dataKeys, ...], [dataValues, ...]).send() -// or -myContract.methods['0x14a6e293']([dataKeys, ...], [dataValues, ...]).send() -``` +### Methods + +Smart contracts implementing the ERC725Y standard MUST implement all of the functions listed below: #### getData ```solidity -function getData(bytes32 dataKey) public view returns(bytes memory) +function getData(bytes32 dataKey) external view returns(bytes memory) ``` Function Selector: `0x54f6127f` @@ -169,60 +200,89 @@ Gets the data set for the given data key. _Parameters:_ -- `dataKey`: the key which value to retrieve. +- `dataKey`: the data key which value to retrieve. -_Returns:_ `bytes` , The data for the requested key. +_Returns:_ `bytes` , The data for the requested data key. #### getData (Array) ```solidity -function getData(bytes32[] memory dataKeys) public view returns(bytes[] memory) +function getData(bytes32[] memory dataKeys) external view returns(bytes[] memory) ``` Function Selector: `0x4e3e6e9c` -Gets array of data at multiple given key. +Gets array of data at multiple given data keys. _Parameters:_ -- `dataKeys`: the keys which values to retrieve. +- `dataKeys`: the data keys which values to retrieve. _Returns:_ `bytes[]` , array of data values for the requested data keys. #### setData ```solidity -function setData(bytes32 dataKey, bytes memory dataValue) public +function setData(bytes32 dataKey, bytes memory dataValue) external ``` Function Selector: `0x7f23690c` -Sets data as bytes in the storage for a single key. MUST only be called by the current owner of the contract. +Sets data as bytes in the storage for a single data key. _Parameters:_ -- `dataKey`: the key which value to set. -- `dataValue`: the data to set. +- `dataKey`: the data key which value to set. +- `dataValue`: the data to store. + +_Requirements:_ + +- MUST only be called by the current owner of the contract. **Triggers Event:** [DataChanged](#datachanged) #### setData (Array) ```solidity -function setData(bytes32[] memory dataKeys, bytes[] memory dataValues) public +function setData(bytes32[] memory dataKeys, bytes[] memory dataValues) external ``` Function Selector: `0x14a6e293` -Sets array of data at multiple keys. MUST only be called by the current owner of the contract. +Sets array of data at multiple data keys. MUST only be called by the current owner of the contract. _Parameters:_ -- `dataKeys`: the keys which values to set. +- `dataKeys`: the data keys which values to set. - `dataValues`: the array of bytes to set. +_Requirements:_ + +- Array parameters MUST have the same length. +- MUST only be called by the current owner of the contract. + **Triggers Event:** [DataChanged](#datachanged) +**Note:** `setData()` and `getData()` uses function overloading, therefore it is better to reference them by the given function signature as follows: + +```js +// web3.js example + +// setData +myContract.methods['setData(bytes32,bytes)'](dataKey, dataValue).send() + +// setData Array +myContract.methods['setData(bytes32[],bytes[])']([dataKeys, ...], [dataValues, ...]).send() + +// OR + +// setData +myContract.methods['0x7f23690c'](dataKey, dataValue).send() + +// setData Array +myContract.methods['0x14a6e293']([dataKeys, ...], [dataValues, ...]).send() +``` + ### Events #### DataChanged @@ -236,19 +296,19 @@ MUST be triggered when a data key was successfully set. ### ERC725Y Data keys Data keys, are the way to retrieve values via `getData()`. These `bytes32` values can be freely chosen, or defined by a standard. -A common ways to define data keys is the hash of a word, e.g. `keccak256('ERCXXXMyNewKeyType')` which results in: `0x6935a24ea384927f250ee0b954ed498cd9203fc5d2bf95c735e52e6ca675e047` +A common way to define data keys is the hash of a word, e.g. `keccak256('ERCXXXMyNewKeyType')` which results in: `0x6935a24ea384927f250ee0b954ed498cd9203fc5d2bf95c735e52e6ca675e047` -The [LSP2 ERC725JSONSchema standard](https://github.com/lukso-network/LIPs/blob/master/LSPs/LSP-2-ERC725YJSONSchema.md) is a more explicit ERC725Y data key standard, that defines key types and value types, and their encoding. +The [LSP2 ERC725JSONSchema standard](https://github.com/lukso-network/LIPs/blob/master/LSPs/LSP-2-ERC725YJSONSchema.md) is a more explicit ERC725Y data key standard, that defines key types and value types, and their encoding and decoding. ## Rationale -Special care went into ensuring that data in this smart contract can be read not only by external applications, but also by other smart contracts in a gas efficient manner. Therefore function overwriting is used to allow for both single and multiple data keys to be retreived. - The generic way of storing data key with values was chosen to allow upgradablity over time. Stored data values can be changed over time. Other smart contract protocols can then interpret this data in new ways and react to interactions from a ERC725 smart contract differently. +The data stored in an ERC725Y smart contract is not only readable/writable by off chain applications, but also by other smart contracts. Function overloading was used to allow for the retrievable for single and multiple keys, to keep gas costs minimal for both uses cases. + ## Backwards Compatibility -All contracts since ERC725v2 from 2018/19 should be compatible to the current version of the standard. Mainly interface ID and Event parameters have changed, while `getData(bytes32[])` and `setData(bytes32[], bytes[])` was added as an efficient way to set/get multiple keys at once. The same applies for execution, as `execute(..[])` was added as an efficient way to batch calls at once. +All contracts since ERC725v2 from 2018/19 should be compatible to the current version of the standard. Mainly interface ID and Event parameters have changed, while `getData(bytes32[])` and `setData(bytes32[], bytes[])` was added as an efficient way to set/get multiple keys at once. The same applies for execution, as `execute(..[])` was added as an efficient way to batch calls. ## Reference Implementation @@ -256,9 +316,9 @@ Reference implementations can be found [here](../assets/eip-725) ## Security Consideration -This contract allows generic executions, therefore special care need to be take care to prevent re-entrancy attacks and other forms for call chain attacks. +This contract allows generic executions, therefore special care need to be take care to prevent re-entrancy attacks and other forms of call chain attacks. -When using the operation type `4` for `delegatecall`, it is important to consider that called contracts can alter the state of the calling contract and also change owner variables at will. Additionally calls to `selfdestruct` are possible and other harmful state changing operations. +When using the operation type `4` for `delegatecall`, it is important to consider that the called contracts can alter the state of the calling contract and also change owner variables and ERC725Y data storge entries at will. Additionally calls to `selfdestruct` are possible and other harmful state changing operations. ### Solidity Interfaces @@ -268,10 +328,10 @@ pragma solidity >=0.5.0 <0.7.0; // ERC165 identifier: `0x570ef073` interface IERC725X /* is ERC165, ERC173 */ { - event ContractCreated(uint256 indexed operationType, address indexed contractAddress, uint256 indexed value); - event Executed(uint256 indexed operationType, address indexed to, uint256 indexed value, bytes4 data); + event ContractCreated(uint256 indexed operationType, address indexed contractAddress, uint256 indexed value, bytes32 salt); + event Executed(uint256 indexed operationType, address indexed target, uint256 indexed value, bytes4 data); - function execute(uint256 operationType, address to, uint256 value, bytes memory data) external payable returns(bytes memory); + function execute(uint256 operationType, address target, uint256 value, bytes memory data) external payable returns(bytes memory); function execute(uint256[] memory operationsType, address[] memory targets, uint256[] memory values, bytes memory datas) external payable returns(bytes[] memory); } @@ -305,3 +365,7 @@ interface IERC725 /* is IERC725X, IERC725Y */ { ## Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + + + +[ERC165]: diff --git a/implementations/.solhint.json b/implementations/.solhint.json index 9a2fb485..f517463c 100644 --- a/implementations/.solhint.json +++ b/implementations/.solhint.json @@ -3,6 +3,7 @@ "rules": { "compiler-version": ["error", "^0.8.0"], "func-visibility": ["error", { "ignoreConstructors": true }], - "reason-string": ["warn", { "maxLength": 120 }] + "reason-string": ["warn", { "maxLength": 120 }], + "no-empty-blocks": ["off"] } } diff --git a/implementations/CHANGELOG.md b/implementations/CHANGELOG.md index 6c0b86d0..523f8fd1 100644 --- a/implementations/CHANGELOG.md +++ b/implementations/CHANGELOG.md @@ -2,6 +2,17 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [4.1.0](https://github.com/ERC725Alliance/ERC725/compare/v4.0.0...v4.1.0) (2022-12-09) + + +### ⚠ BREAKING CHANGES + +* add `salt` parameter to the `ContractCreated` event in ERC725X (#183) + + +* create internal virtual `_execute` function with core ERC725X logic to improve overriding core behaviour ([#184](https://github.com/ERC725Alliance/ERC725/pull/184)) +* add `salt` parameter to the `ContractCreated` event in ERC725X ([#183](https://github.com/ERC725Alliance/ERC725/issues/183)) ([4a6ca14](https://github.com/ERC725Alliance/ERC725/commit/4a6ca140b35f1d78f6fefedb11ddc3981f71acb6)) + ## 4.0.0 (2022-10-31) ### ⚠ BREAKING CHANGES diff --git a/implementations/contracts/ERC725XCore.sol b/implementations/contracts/ERC725XCore.sol index e2a6d9c6..7f6235ed 100644 --- a/implementations/contracts/ERC725XCore.sol +++ b/implementations/contracts/ERC725XCore.sol @@ -43,9 +43,6 @@ abstract contract ERC725XCore is OwnableUnset, ERC165, IERC725X { uint256 value, bytes memory data ) public payable virtual onlyOwner returns (bytes memory) { - if (address(this).balance < value) { - revert ERC725X_InsufficientBalance(address(this).balance, value); - } return _execute(operationType, target, value, data); } @@ -57,37 +54,22 @@ abstract contract ERC725XCore is OwnableUnset, ERC165, IERC725X { address[] memory targets, uint256[] memory values, bytes[] memory datas - ) public payable virtual onlyOwner returns (bytes[] memory result) { - if ( - operationsType.length != targets.length || - (targets.length != values.length || values.length != datas.length) - ) revert ERC725X_ExecuteParametersLengthMismatch(); - - result = new bytes[](operationsType.length); - for (uint256 i = 0; i < operationsType.length; i = _uncheckedIncrementERC725X(i)) { - if (address(this).balance < values[i]) - revert ERC725X_InsufficientBalance(address(this).balance, values[i]); - - result[i] = _execute(operationsType[i], targets[i], values[i], datas[i]); - } + ) public payable virtual onlyOwner returns (bytes[] memory) { + return _execute(operationsType, targets, values, datas); } /** * @inheritdoc ERC165 */ - function supportsInterface(bytes4 interfaceId) - public - view - virtual - override(IERC165, ERC165) - returns (bool) - { + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(IERC165, ERC165) returns (bool) { return interfaceId == _INTERFACEID_ERC725X || super.supportsInterface(interfaceId); } /** * @dev check the `operationType` provided and perform the associated low-level opcode. - * see `IERC725X.execute(...)`. + * see `IERC725X.execute(uint256,address,uint256,bytes)`. */ function _execute( uint256 operationType, @@ -138,6 +120,30 @@ abstract contract ERC725XCore is OwnableUnset, ERC165, IERC725X { revert ERC725X_UnknownOperationType(operationType); } + /** + * @dev same as `_execute` but for batch execution + * see `IERC725X,execute(uint256[],address[],uint256[],bytes[])` + */ + function _execute( + uint256[] memory operationsType, + address[] memory targets, + uint256[] memory values, + bytes[] memory datas + ) internal virtual returns (bytes[] memory) { + if ( + operationsType.length != targets.length || + (targets.length != values.length || values.length != datas.length) + ) revert ERC725X_ExecuteParametersLengthMismatch(); + + bytes[] memory result = new bytes[](operationsType.length); + + for (uint256 i = 0; i < operationsType.length; i = _uncheckedIncrementERC725X(i)) { + result[i] = _execute(operationsType[i], targets[i], values[i], datas[i]); + } + + return result; + } + /** * @dev perform low-level call (operation type = 0) * @param target The address on which call is executed @@ -150,9 +156,13 @@ abstract contract ERC725XCore is OwnableUnset, ERC165, IERC725X { uint256 value, bytes memory data ) internal virtual returns (bytes memory result) { + if (address(this).balance < value) { + revert ERC725X_InsufficientBalance(address(this).balance, value); + } + emit Executed(OPERATION_0_CALL, target, value, bytes4(data)); - // solhint-disable avoid-low-level-calls + // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returnData) = target.call{value: value}(data); result = Address.verifyCallResult(success, returnData, "ERC725X: Unknown Error"); } @@ -163,14 +173,13 @@ abstract contract ERC725XCore is OwnableUnset, ERC165, IERC725X { * @param data The data to be sent with the staticcall * @return result The data returned from the staticcall */ - function _executeStaticCall(address target, bytes memory data) - internal - virtual - returns (bytes memory result) - { + function _executeStaticCall( + address target, + bytes memory data + ) internal virtual returns (bytes memory result) { emit Executed(OPERATION_3_STATICCALL, target, 0, bytes4(data)); - // solhint-disable avoid-low-level-calls + // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returnData) = target.staticcall(data); result = Address.verifyCallResult(success, returnData, "ERC725X: Unknown Error"); } @@ -181,14 +190,13 @@ abstract contract ERC725XCore is OwnableUnset, ERC165, IERC725X { * @param data The data to be sent with the delegatecall * @return result The data returned from the delegatecall */ - function _executeDelegateCall(address target, bytes memory data) - internal - virtual - returns (bytes memory result) - { + function _executeDelegateCall( + address target, + bytes memory data + ) internal virtual returns (bytes memory result) { emit Executed(OPERATION_4_DELEGATECALL, target, 0, bytes4(data)); - // solhint-disable avoid-low-level-calls + // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returnData) = target.delegatecall(data); result = Address.verifyCallResult(success, returnData, "ERC725X: Unknown Error"); } @@ -199,17 +207,20 @@ abstract contract ERC725XCore is OwnableUnset, ERC165, IERC725X { * @param creationCode The contract creation bytecode to deploy appended with the constructor argument(s) * @return newContract The address of the contract created as bytes */ - function _deployCreate(uint256 value, bytes memory creationCode) - internal - virtual - returns (bytes memory newContract) - { + function _deployCreate( + uint256 value, + bytes memory creationCode + ) internal virtual returns (bytes memory newContract) { + if (address(this).balance < value) { + revert ERC725X_InsufficientBalance(address(this).balance, value); + } + if (creationCode.length == 0) { revert ERC725X_NoContractBytecodeProvided(); } address contractAddress; - // solhint-disable no-inline-assembly + // solhint-disable-next-line no-inline-assembly assembly { contractAddress := create(value, add(creationCode, 0x20), mload(creationCode)) } @@ -219,7 +230,7 @@ abstract contract ERC725XCore is OwnableUnset, ERC165, IERC725X { } newContract = abi.encodePacked(contractAddress); - emit ContractCreated(OPERATION_1_CREATE, contractAddress, value); + emit ContractCreated(OPERATION_1_CREATE, contractAddress, value, bytes32(0)); } /** @@ -228,11 +239,10 @@ abstract contract ERC725XCore is OwnableUnset, ERC165, IERC725X { * @param creationCode The contract creation bytecode to deploy appended with the constructor argument(s) and a bytes32 salt * @return newContract The address of the contract created as bytes */ - function _deployCreate2(uint256 value, bytes memory creationCode) - internal - virtual - returns (bytes memory newContract) - { + function _deployCreate2( + uint256 value, + bytes memory creationCode + ) internal virtual returns (bytes memory newContract) { if (creationCode.length == 0) { revert ERC725X_NoContractBytecodeProvided(); } @@ -242,7 +252,7 @@ abstract contract ERC725XCore is OwnableUnset, ERC165, IERC725X { address contractAddress = Create2.deploy(value, salt, bytecode); newContract = abi.encodePacked(contractAddress); - emit ContractCreated(OPERATION_2_CREATE2, contractAddress, value); + emit ContractCreated(OPERATION_2_CREATE2, contractAddress, value, salt); } /** diff --git a/implementations/contracts/helpers/Counter.sol b/implementations/contracts/helpers/Counter.sol index a40ce74b..3b06a42e 100644 --- a/implementations/contracts/helpers/Counter.sol +++ b/implementations/contracts/helpers/Counter.sol @@ -23,6 +23,6 @@ contract Counter { return count; } - // solhint-disable no-empty-blocks + // solhint-disable-next-line no-empty-blocks receive() external payable {} } diff --git a/implementations/contracts/helpers/CustomRevertTest.sol b/implementations/contracts/helpers/CustomRevertTest.sol index eb31e2ec..98177bf8 100644 --- a/implementations/contracts/helpers/CustomRevertTest.sol +++ b/implementations/contracts/helpers/CustomRevertTest.sol @@ -4,10 +4,11 @@ pragma solidity ^0.8.0; /** * @dev Contract used for testing implementing a receive function that reverts; */ -contract revertTester { +contract RevertTester { error MyCustomError(address sender, address initiater); receive() external payable { + // solhint-disable-next-line avoid-tx-origin revert MyCustomError(msg.sender, tx.origin); } @@ -20,10 +21,12 @@ contract revertTester { } function revertMeWithCustomErrorView() public { + // solhint-disable-next-line avoid-tx-origin revert MyCustomError(msg.sender, tx.origin); } function revertMeWithCustomErrorNotView() public { + // solhint-disable-next-line avoid-tx-origin revert MyCustomError(msg.sender, tx.origin); } } diff --git a/implementations/contracts/helpers/ERC725XPayableTester.sol b/implementations/contracts/helpers/ERC725XPayableTester.sol index 912c825a..eb078580 100644 --- a/implementations/contracts/helpers/ERC725XPayableTester.sol +++ b/implementations/contracts/helpers/ERC725XPayableTester.sol @@ -11,9 +11,9 @@ import "../ERC725X.sol"; * 2. the contract can then transfer the native tokens in its balance via ERC725X.execute(0, recipient, amount, "") */ contract ERC725XPayableTester is ERC725X { - // solhint-disable no-empty-blocks + // solhint-disable-next-line no-empty-blocks constructor(address _newOwner) ERC725X(_newOwner) {} - // solhint-disable no-empty-blocks + // solhint-disable-next-line no-empty-blocks receive() external payable {} } diff --git a/implementations/contracts/helpers/NonPayableFallbackContract.sol b/implementations/contracts/helpers/NonPayableFallbackContract.sol index f6cb71e0..f37b86a5 100644 --- a/implementations/contracts/helpers/NonPayableFallbackContract.sol +++ b/implementations/contracts/helpers/NonPayableFallbackContract.sol @@ -5,5 +5,6 @@ pragma solidity ^0.8.0; * @dev Contract used for testing implementing receive(); */ contract NonPayableFallbackContract { + // solhint-disable-next-line payable-fallback fallback() external {} } diff --git a/implementations/contracts/interfaces/IERC725X.sol b/implementations/contracts/interfaces/IERC725X.sol index b4d9c718..3f13ae45 100644 --- a/implementations/contracts/interfaces/IERC725X.sol +++ b/implementations/contracts/interfaces/IERC725X.sol @@ -20,7 +20,8 @@ interface IERC725X is IERC165 { event ContractCreated( uint256 indexed operationType, address indexed contractAddress, - uint256 indexed value + uint256 indexed value, + bytes32 salt ); /** diff --git a/implementations/package-lock.json b/implementations/package-lock.json index 27136434..b10a596e 100644 --- a/implementations/package-lock.json +++ b/implementations/package-lock.json @@ -1,12 +1,12 @@ { "name": "@erc725/smart-contracts", - "version": "4.0.0", + "version": "4.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@erc725/smart-contracts", - "version": "4.0.0", + "version": "4.1.0", "license": "Apache-2.0", "dependencies": { "@openzeppelin/contracts": "^4.7.3", diff --git a/implementations/package.json b/implementations/package.json index c446af6d..c131207a 100644 --- a/implementations/package.json +++ b/implementations/package.json @@ -1,6 +1,6 @@ { "name": "@erc725/smart-contracts", - "version": "4.0.0", + "version": "4.1.0", "description": "ERC725 contract implementations", "homepage": "https://erc725alliance.org", "repository": { diff --git a/implementations/test/ERC725X.behaviour.ts b/implementations/test/ERC725X.behaviour.ts index fae89b61..f30ebb21 100644 --- a/implementations/test/ERC725X.behaviour.ts +++ b/implementations/test/ERC725X.behaviour.ts @@ -855,7 +855,8 @@ export const shouldBehaveLikeERC725X = ( .withArgs( txParams.Operation, addressContractCreatedChecksumed, - txParams.value + txParams.value, + ethers.utils.hexZeroPad("0x00", 32) ); const codeRetreived = await provider.getCode( @@ -913,7 +914,8 @@ export const shouldBehaveLikeERC725X = ( .withArgs( txParams.Operation, addressContractCreatedChecksumed, - txParams.value + txParams.value, + ethers.utils.hexZeroPad("0x00", 32) ); const codeRetreived = await provider.getCode( @@ -962,7 +964,8 @@ export const shouldBehaveLikeERC725X = ( .withArgs( txParams.Operation, addressContractCreatedChecksumed, - txParams.value + txParams.value, + ethers.utils.hexZeroPad("0x00", 32) ); const codeRetreived = await provider.getCode( @@ -1064,7 +1067,8 @@ export const shouldBehaveLikeERC725X = ( .withArgs( txParams.Operation, addressContractCreatedChecksumed, - txParams.value + txParams.value, + ethers.utils.hexZeroPad("0x00", 32) ); const codeRetreived = await provider.getCode( @@ -1179,7 +1183,8 @@ export const shouldBehaveLikeERC725X = ( .withArgs( txParams.Operation, addressContractCreatedChecksumed, - txParams.value + txParams.value, + salt ); const codeRetreived = await provider.getCode( @@ -1241,7 +1246,8 @@ export const shouldBehaveLikeERC725X = ( .withArgs( txParams.Operation, addressContractCreatedChecksumed, - txParams.value + txParams.value, + salt ); const codeRetreived = await provider.getCode( @@ -1294,7 +1300,8 @@ export const shouldBehaveLikeERC725X = ( .withArgs( txParams.Operation, addressContractCreatedChecksumed, - txParams.value + txParams.value, + salt ); const codeRetreived = await provider.getCode( @@ -1431,7 +1438,8 @@ export const shouldBehaveLikeERC725X = ( .withArgs( txParams.Operation, addressContractCreatedChecksumed, - txParams.value + txParams.value, + salt ); const codeRetreived = await provider.getCode( @@ -1469,9 +1477,8 @@ export const shouldBehaveLikeERC725X = ( txParams.value, txParams.data ) - ).to.be.revertedWithCustomError( - context.erc725X, - "ERC725X_InsufficientBalance" + ).to.be.revertedWith( + "Create2: insufficient balance" ); }); }); @@ -1542,7 +1549,8 @@ export const shouldBehaveLikeERC725X = ( .withArgs( txParams.Operation, addressContractCreatedChecksumed, - txParams.value + txParams.value, + salt ); const codeRetreived = await provider.getCode( @@ -2275,7 +2283,8 @@ export const shouldBehaveLikeERC725X = ( .withArgs( txParams.Operations[1], contractAddress, - txParams.values[1] + txParams.values[1], + ethers.utils.hexZeroPad("0x00", 32) ); const codeOfContractCreated = await provider.getCode( @@ -2400,13 +2409,15 @@ export const shouldBehaveLikeERC725X = ( .withArgs( txParams.Operations[0], ethers.utils.getAddress(contractsAddresses[0]), - txParams.values[0] + txParams.values[0], + ethers.utils.hexZeroPad("0x00", 32) ) .to.emit(context.erc725X, "ContractCreated") .withArgs( txParams.Operations[1], ethers.utils.getAddress(contractsAddresses[1]), - txParams.values[1] + txParams.values[1], + ethers.utils.hexZeroPad("0x00", 32) ); const codeOfContractCreated1 = await provider.getCode(