From b6a0193e704a60b8328de0bccc2c94a865122463 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Mon, 30 Oct 2023 18:05:38 +0000 Subject: [PATCH 01/22] build: fix explorer API url to verify contracts --- hardhat.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index e9934cee8..fa0b73099 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -79,7 +79,7 @@ const config: HardhatUserConfig = { network: 'luksoTestnet', chainId: 4201, urls: { - apiURL: 'https://explorer.execution.testnet.lukso.network/api', + apiURL: 'https://api.explorer.execution.testnet.lukso.network/api', browserURL: 'https://explorer.execution.testnet.lukso.network/', }, }, From d20f17d91e58c26d9534d084e8d5b35a3358d93c Mon Sep 17 00:00:00 2001 From: Maxime Date: Wed, 1 Nov 2023 12:44:43 +0000 Subject: [PATCH 02/22] refactor: update isValidNonce function --- contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.sol | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.sol b/contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.sol index fb8c99015..7e43cb9d9 100644 --- a/contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.sol +++ b/contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.sol @@ -139,10 +139,6 @@ abstract contract LSP25MultiChannelNonce { address from, uint256 idx ) internal view virtual returns (bool) { - uint256 mask = ~uint128(0); - // Alternatively: - // uint256 mask = (1<<128)-1; - // uint256 mask = 0xffffffffffffffffffffffffffffffff; - return (idx & mask) == _nonceStore[from][idx >> 128]; + return uint128(idx) == _nonceStore[from][idx >> 128]; } } From 2f1c4da05a71103f83695c5ca3f2992c4b4bbe68 Mon Sep 17 00:00:00 2001 From: Maxime Date: Wed, 1 Nov 2023 12:45:12 +0000 Subject: [PATCH 03/22] refactor: update for loop --- contracts/LSP6KeyManager/LSP6KeyManagerCore.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol b/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol index 54513b0af..f8ea578e4 100644 --- a/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol +++ b/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol @@ -612,7 +612,7 @@ abstract contract LSP6KeyManagerCore is revert ERC725X_ExecuteParametersEmptyArray(); } - for (uint256 ii = 0; ii < operationTypes.length; ii++) { + for (uint256 ii; ii < operationTypes.length; ii++) { LSP6ExecuteModule._verifyCanExecute( targetContract, from, From b61c7f0e1836de5a04d9add1f9b677fa3e898c73 Mon Sep 17 00:00:00 2001 From: b00ste Date: Wed, 1 Nov 2023 13:28:46 +0200 Subject: [PATCH 04/22] test: add tests for LSP1 Type IDs --- contracts/LSP0ERC725Account/LSP0Constants.sol | 2 +- contracts/LSP9Vault/LSP9Constants.sol | 2 +- contracts/Mocks/LSP1TypeIDsTester.sol | 108 ++++++++++++++++++ tests/Mocks/LSP1TypeIDs.test.ts | 34 ++++++ 4 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 contracts/Mocks/LSP1TypeIDsTester.sol create mode 100644 tests/Mocks/LSP1TypeIDs.test.ts diff --git a/contracts/LSP0ERC725Account/LSP0Constants.sol b/contracts/LSP0ERC725Account/LSP0Constants.sol index 600f9426a..31a4d4ddb 100644 --- a/contracts/LSP0ERC725Account/LSP0Constants.sol +++ b/contracts/LSP0ERC725Account/LSP0Constants.sol @@ -14,7 +14,7 @@ bytes4 constant _ERC1271_FAILVALUE = 0xffffffff; // keccak256('LSP0ValueReceived') bytes32 constant _TYPEID_LSP0_VALUE_RECEIVED = 0x9c4705229491d365fb5434052e12a386d6771d976bea61070a8c694e8affea3d; -// Ownerhsip Transfer Type IDs +// Ownership Transfer Type IDs // keccak256('LSP0OwnershipTransferStarted') bytes32 constant _TYPEID_LSP0_OwnershipTransferStarted = 0xe17117c9d2665d1dbeb479ed8058bbebde3c50ac50e2e65619f60006caac6926; diff --git a/contracts/LSP9Vault/LSP9Constants.sol b/contracts/LSP9Vault/LSP9Constants.sol index f2ddb03d9..943120b6d 100644 --- a/contracts/LSP9Vault/LSP9Constants.sol +++ b/contracts/LSP9Vault/LSP9Constants.sol @@ -17,7 +17,7 @@ bytes constant _LSP9_SUPPORTED_STANDARDS_VALUE = hex"7c0334a1"; // keccak256('LSP9ValueReceived') bytes32 constant _TYPEID_LSP9_VALUE_RECEIVED = 0x468cd1581d7bc001c3b685513d2b929b55437be34700410383d58f3aa1ea0abc; -// Ownerhsip Transfer Type IDs +// Ownership Transfer Type IDs // keccak256('LSP9OwnershipTransferStarted') bytes32 constant _TYPEID_LSP9_OwnershipTransferStarted = 0xaefd43f45fed1bcd8992f23c803b6f4ec45cf6b62b0d404d565f290a471e763f; diff --git a/contracts/Mocks/LSP1TypeIDsTester.sol b/contracts/Mocks/LSP1TypeIDsTester.sol new file mode 100644 index 000000000..ac9615aba --- /dev/null +++ b/contracts/Mocks/LSP1TypeIDsTester.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.4; + +import { + _TYPEID_LSP0_VALUE_RECEIVED, + _TYPEID_LSP0_OwnershipTransferStarted, + _TYPEID_LSP0_OwnershipTransferred_SenderNotification, + _TYPEID_LSP0_OwnershipTransferred_RecipientNotification +} from "../LSP0ERC725Account/LSP0Constants.sol"; +import { + _TYPEID_LSP7_TOKENSSENDER, + _TYPEID_LSP7_TOKENSRECIPIENT, + _TYPEID_LSP7_TOKENOPERATOR +} from "../LSP7DigitalAsset/LSP7Constants.sol"; +import { + _TYPEID_LSP8_TOKENSSENDER, + _TYPEID_LSP8_TOKENSRECIPIENT, + _TYPEID_LSP8_TOKENOPERATOR +} from "../LSP8IdentifiableDigitalAsset/LSP8Constants.sol"; +import { + _TYPEID_LSP9_VALUE_RECEIVED, + _TYPEID_LSP9_OwnershipTransferStarted, + _TYPEID_LSP9_OwnershipTransferred_SenderNotification, + _TYPEID_LSP9_OwnershipTransferred_RecipientNotification +} from "../LSP9Vault/LSP9Constants.sol"; +import { + _TYPEID_LSP14_OwnershipTransferStarted, + _TYPEID_LSP14_OwnershipTransferred_SenderNotification, + _TYPEID_LSP14_OwnershipTransferred_RecipientNotification +} from "../LSP14Ownable2Step/LSP14Constants.sol"; + +error LSP1TypeIdHashIsWrong(bytes32 typeIdHash, string typeIdname); + +contract LSP1TypeIDsTester { + mapping(string => bytes32) private _typeIds; + + constructor() { + // ------ LSP0 ------ + _typeIds["LSP0ValueReceived"] = _TYPEID_LSP0_VALUE_RECEIVED; + _typeIds[ + "LSP0OwnershipTransferStarted" + ] = _TYPEID_LSP0_OwnershipTransferStarted; + _typeIds[ + "LSP0OwnershipTransferred_SenderNotification" + ] = _TYPEID_LSP0_OwnershipTransferred_SenderNotification; + _typeIds[ + "LSP0OwnershipTransferred_RecipientNotification" + ] = _TYPEID_LSP0_OwnershipTransferred_RecipientNotification; + // ------------------ + + // ------ LSP7 ------ + _typeIds["LSP7Tokens_SenderNotification"] = _TYPEID_LSP7_TOKENSSENDER; + _typeIds[ + "LSP7Tokens_RecipientNotification" + ] = _TYPEID_LSP7_TOKENSRECIPIENT; + _typeIds[ + "LSP7Tokens_OperatorNotification" + ] = _TYPEID_LSP7_TOKENOPERATOR; + // ------------------ + + // ------ LSP8 ------ + _typeIds["LSP8Tokens_SenderNotification"] = _TYPEID_LSP8_TOKENSSENDER; + _typeIds[ + "LSP8Tokens_RecipientNotification" + ] = _TYPEID_LSP8_TOKENSRECIPIENT; + _typeIds[ + "LSP8Tokens_OperatorNotification" + ] = _TYPEID_LSP8_TOKENOPERATOR; + // ------------------ + + // ------ LSP9 ------ + _typeIds["LSP9ValueReceived"] = _TYPEID_LSP9_VALUE_RECEIVED; + _typeIds[ + "LSP9OwnershipTransferStarted" + ] = _TYPEID_LSP9_OwnershipTransferStarted; + _typeIds[ + "LSP9OwnershipTransferred_SenderNotification" + ] = _TYPEID_LSP9_OwnershipTransferred_SenderNotification; + _typeIds[ + "LSP9OwnershipTransferred_RecipientNotification" + ] = _TYPEID_LSP9_OwnershipTransferred_RecipientNotification; + // ------------------ + + // ------ LSP14 ------ + _typeIds[ + "LSP14OwnershipTransferStarted" + ] = _TYPEID_LSP14_OwnershipTransferStarted; + _typeIds[ + "LSP14OwnershipTransferred_SenderNotification" + ] = _TYPEID_LSP14_OwnershipTransferred_SenderNotification; + _typeIds[ + "LSP14OwnershipTransferred_RecipientNotification" + ] = _TYPEID_LSP14_OwnershipTransferred_RecipientNotification; + // ------------------- + } + + function verifyLSP1TypeID( + string calldata typeIdName + ) public view returns (bytes32) { + bytes32 typeIdHash = _typeIds[typeIdName]; + + if (typeIdHash != keccak256(bytes(typeIdName))) { + revert LSP1TypeIdHashIsWrong(typeIdHash, typeIdName); + } + + return typeIdHash; + } +} diff --git a/tests/Mocks/LSP1TypeIDs.test.ts b/tests/Mocks/LSP1TypeIDs.test.ts new file mode 100644 index 000000000..3c00401cf --- /dev/null +++ b/tests/Mocks/LSP1TypeIDs.test.ts @@ -0,0 +1,34 @@ +import { ethers } from 'hardhat'; +import { expect } from 'chai'; +import { hexlify, keccak256, toUtf8Bytes } from 'ethers/lib/utils'; +import { LSP1TypeIDsTester, LSP1TypeIDsTester__factory } from '../../types'; +import { LSP1_TYPE_IDS } from '../../constants'; + +describe('calculate LSP1 Type IDs', () => { + const LSP1TypeIds = Object.entries(LSP1_TYPE_IDS); + + describe('Testing javascript constants', () => { + LSP1TypeIds.forEach(([typeIdName, typeIdHash]) => + it(`Testing LSP1 Type ID: ${typeIdName}`, async () => { + expect(typeIdHash).to.equal(hexlify(keccak256(toUtf8Bytes(typeIdName)))); + }), + ); + }); + + describe('Testing solidity constants', () => { + let LSP1TypeIDsTester: LSP1TypeIDsTester; + + before('Deploying `LSP1TypeIDs` tester contract', async () => { + const signers = await ethers.getSigners(); + LSP1TypeIDsTester = await new LSP1TypeIDsTester__factory(signers[0]).deploy(); + }); + + LSP1TypeIds.forEach(([typeIdName, typeIdHash]) => + it(`Testing LSP1 Type ID: ${typeIdName}`, async () => { + const returnedTypeId = await LSP1TypeIDsTester.verifyLSP1TypeID(typeIdName); + + expect(returnedTypeId).to.equal(typeIdHash); + }), + ); + }); +}); From fd23a24859f72e3ea9312ae1761ab98709db0142 Mon Sep 17 00:00:00 2001 From: Jean Cvllr <31145285+CJ42@users.noreply.github.com> Date: Thu, 2 Nov 2023 13:39:01 +0000 Subject: [PATCH 05/22] fix: [L-01] Return `bytes` directly in LSP6 `_executePayload`, to prevent data loss (#784) * fix: return raw bytes in LSP6, not decoded as `bytes` * test: adjust tests to decode `bytes` / `bytes[]` first before decoding target type * test: add more tests for return values of `executeRelayCall` and `executeRelayCallBatch` --- .../LSP6KeyManager/LSP6KeyManagerCore.sol | 3 +- contracts/Mocks/TargetContract.sol | 11 + .../Interactions/AllowedStandards.test.ts | 18 +- .../Interactions/BatchExecute.test.ts | 9 +- .../Interactions/PermissionCall.test.ts | 8 +- .../Interactions/PermissionDeploy.test.ts | 28 ++- .../Interactions/PermissionStaticCall.test.ts | 194 +++++++++++------- .../Relay/ExecuteRelayCall.test.ts | 179 +++++++++++++++- 8 files changed, 358 insertions(+), 92 deletions(-) diff --git a/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol b/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol index f8ea578e4..9edf51cff 100644 --- a/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol +++ b/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol @@ -510,13 +510,14 @@ abstract contract LSP6KeyManagerCore is value: msgValue, gas: gasleft() }(payload); + bytes memory result = Address.verifyCallResult( success, returnData, "LSP6: failed executing payload" ); - return result.length != 0 ? abi.decode(result, (bytes)) : result; + return result; } /** diff --git a/contracts/Mocks/TargetContract.sol b/contracts/Mocks/TargetContract.sol index 225bba71f..e4a01750d 100644 --- a/contracts/Mocks/TargetContract.sol +++ b/contracts/Mocks/TargetContract.sol @@ -38,4 +38,15 @@ contract TargetContract { function revertCall() public pure { revert("TargetContract:revertCall: this function has reverted!"); } + + function getDynamicArrayOf2Numbers() + public + pure + returns (uint256[] memory) + { + uint256[] memory results = new uint256[](2); + results[0] = 10; + results[1] = 20; + return results; + } } diff --git a/tests/LSP6KeyManager/Interactions/AllowedStandards.test.ts b/tests/LSP6KeyManager/Interactions/AllowedStandards.test.ts index ebb34b810..5943849ea 100644 --- a/tests/LSP6KeyManager/Interactions/AllowedStandards.test.ts +++ b/tests/LSP6KeyManager/Interactions/AllowedStandards.test.ts @@ -137,11 +137,14 @@ export const shouldBehaveLikeAllowedStandards = (buildContext: () => Promise { @@ -181,11 +184,14 @@ export const shouldBehaveLikeAllowedStandards = (buildContext: () => Promise { - it('should pass and return data', async () => { + it('should pass and return a `string`', async () => { const expectedName = await targetContract.callStatic.getName(); const targetContractPayload = targetContract.interface.encodeFunctionData('getName'); @@ -111,16 +111,40 @@ export const shouldBehaveLikePermissionStaticCall = ( .connect(context.mainController) .callStatic.execute(executePayload); - const [decodedResult] = abiCoder.decode(['string'], result); + const [decodedBytes] = abiCoder.decode(['bytes'], result); + + const [decodedResult] = abiCoder.decode(['string'], decodedBytes); expect(decodedResult).to.equal(expectedName); }); + + it('should pass and return an array of number `uint256[]`', async () => { + const expectedNumbers = await targetContract.callStatic.getDynamicArrayOf2Numbers(); + + const targetContractPayload = targetContract.interface.encodeFunctionData( + 'getDynamicArrayOf2Numbers', + ); + + const executePayload = context.universalProfile.interface.encodeFunctionData('execute', [ + OPERATION_TYPES.STATICCALL, + targetContract.address, + 0, + targetContractPayload, + ]); + + const result = await context.keyManager + .connect(context.mainController) + .callStatic.execute(executePayload); + + const [decodedBytes] = abiCoder.decode(['bytes'], result); + + const [decodedResult] = abiCoder.decode(['uint256[]'], decodedBytes); + expect(decodedResult).to.deep.equal(expectedNumbers); + }); }); describe('when caller has permission STATICCALL + some allowed calls', () => { describe('when calling a `view` function on a target contract', () => { it('should pass and return data when `value` param is 0 ', async () => { - const expectedName = await targetContract.callStatic.getName(); - const targetContractPayload = targetContract.interface.encodeFunctionData('getName'); const executePayload = context.universalProfile.interface.encodeFunctionData('execute', [ @@ -134,7 +158,11 @@ export const shouldBehaveLikePermissionStaticCall = ( .connect(addressCanMakeStaticCall) .callStatic.execute(executePayload); - const [decodedResult] = abiCoder.decode(['string'], result); + const [decodedBytes] = abiCoder.decode(['bytes'], result); + + const expectedName = await targetContract.callStatic.getName(); + + const [decodedResult] = abiCoder.decode(['string'], decodedBytes); expect(decodedResult).to.equal(expectedName); }); @@ -182,8 +210,10 @@ export const shouldBehaveLikePermissionStaticCall = ( .connect(addressCanMakeStaticCall) .callStatic.execute(executePayload); - const [decodedResult] = abiCoder.decode(['bytes4'], result); - expect(decodedResult).to.equal(ERC1271_VALUES.SUCCESS_VALUE); + const [decodedBytes] = abiCoder.decode(['bytes'], result); + + const [decodedBytes4] = abiCoder.decode(['bytes4'], decodedBytes); + expect(decodedBytes4).to.equal(ERC1271_VALUES.SUCCESS_VALUE); }); it('should revert with error `ERC725X_MsgValueDisallowedInStaticCall` if `value` param is not 0', async () => { @@ -228,7 +258,8 @@ export const shouldBehaveLikePermissionStaticCall = ( ); // the important part is that the function is `view` and return the correct value - const expectedReturnValue = '0x150b7a02'; + const expectedReturnValue = + onERC721ReceivedContract.interface.getSighash('onERC721Received'); const executePayload = context.universalProfile.interface.encodeFunctionData('execute', [ OPERATION_TYPES.STATICCALL, @@ -241,8 +272,10 @@ export const shouldBehaveLikePermissionStaticCall = ( .connect(addressCanMakeStaticCall) .callStatic.execute(executePayload); - const [decodedResult] = abiCoder.decode(['bytes4'], result); - expect(decodedResult).to.equal(expectedReturnValue); + const [decodedBytes] = abiCoder.decode(['bytes'], result); + + const [decodedBytes4] = abiCoder.decode(['bytes4'], decodedBytes); + expect(decodedBytes4).to.equal(expectedReturnValue); }); }); @@ -274,52 +307,53 @@ export const shouldBehaveLikePermissionStaticCall = ( 'ERC725X_MsgValueDisallowedInStaticCall', ); }); - }), - describe('when calling a state changing function at the target contract', () => { - it('should revert (silently) if `value` parameter is 0', async () => { - const initialValue = await targetContract.callStatic.getName(); + }); - const targetContractPayload = targetContract.interface.encodeFunctionData('setName', [ - 'modified name', - ]); + describe('when calling a state changing function at the target contract', () => { + it('should revert (silently) if `value` parameter is 0', async () => { + const initialValue = await targetContract.callStatic.getName(); - const executePayload = context.universalProfile.interface.encodeFunctionData('execute', [ - OPERATION_TYPES.STATICCALL, - targetContract.address, - 0, - targetContractPayload, - ]); + const targetContractPayload = targetContract.interface.encodeFunctionData('setName', [ + 'modified name', + ]); - await expect(context.keyManager.connect(addressCanMakeStaticCall).execute(executePayload)) - .to.be.reverted; + const executePayload = context.universalProfile.interface.encodeFunctionData('execute', [ + OPERATION_TYPES.STATICCALL, + targetContract.address, + 0, + targetContractPayload, + ]); - // ensure state hasn't changed. - const newValue = await targetContract.callStatic.getName(); - expect(initialValue).to.equal(newValue); - }); + await expect(context.keyManager.connect(addressCanMakeStaticCall).execute(executePayload)) + .to.be.reverted; - it('should revert with error `ERC725X_MsgValueDisallowedInStaticCall` if `value` parameter is not 0', async () => { - const lyxAmount = ethers.utils.parseEther('3'); + // ensure state hasn't changed. + const newValue = await targetContract.callStatic.getName(); + expect(initialValue).to.equal(newValue); + }); - const targetContractPayload = targetContract.interface.encodeFunctionData('setName', [ - 'modified name', - ]); + it('should revert with error `ERC725X_MsgValueDisallowedInStaticCall` if `value` parameter is not 0', async () => { + const lyxAmount = ethers.utils.parseEther('3'); - const executePayload = context.universalProfile.interface.encodeFunctionData('execute', [ - OPERATION_TYPES.STATICCALL, - targetContract.address, - lyxAmount, - targetContractPayload, - ]); + const targetContractPayload = targetContract.interface.encodeFunctionData('setName', [ + 'modified name', + ]); - await expect( - context.keyManager.connect(addressCanMakeStaticCall).execute(executePayload), - ).to.be.revertedWithCustomError( - context.universalProfile, - 'ERC725X_MsgValueDisallowedInStaticCall', - ); - }); + const executePayload = context.universalProfile.interface.encodeFunctionData('execute', [ + OPERATION_TYPES.STATICCALL, + targetContract.address, + lyxAmount, + targetContractPayload, + ]); + + await expect( + context.keyManager.connect(addressCanMakeStaticCall).execute(executePayload), + ).to.be.revertedWithCustomError( + context.universalProfile, + 'ERC725X_MsgValueDisallowedInStaticCall', + ); }); + }); }); describe('when caller has permission STATICCALL + no allowed calls', () => { @@ -415,8 +449,6 @@ export const shouldBehaveLikePermissionStaticCall = ( it('should allow to call view function -> getName()', async () => { const targetContract = allowedTargetContracts[0]; - const name = await targetContract.getName(); - const payload = context.universalProfile.interface.encodeFunctionData('execute', [ OPERATION_TYPES.STATICCALL, targetContract.address, @@ -426,15 +458,17 @@ export const shouldBehaveLikePermissionStaticCall = ( const result = await context.keyManager.connect(caller).callStatic.execute(payload); - const [decodedResult] = abiCoder.decode(['string'], result); - expect(decodedResult).to.equal(name); + const [decodedResult] = abiCoder.decode(['bytes'], result); + + const expectedName = await targetContract.getName(); + + const [decodedString] = abiCoder.decode(['string'], decodedResult); + expect(decodedString).to.equal(expectedName); }); it('should allow to call view function -> getNumber()', async () => { const targetContract = allowedTargetContracts[0]; - const number = await targetContract.getNumber(); - const payload = context.universalProfile.interface.encodeFunctionData('execute', [ OPERATION_TYPES.STATICCALL, targetContract.address, @@ -444,8 +478,12 @@ export const shouldBehaveLikePermissionStaticCall = ( const result = await context.keyManager.connect(caller).callStatic.execute(payload); - const [decodedResult] = abiCoder.decode(['uint256'], result); - expect(decodedResult).to.equal(number); + const [decodedResult] = abiCoder.decode(['bytes'], result); + + const expectedNumber = await targetContract.getNumber(); + + const [decodedNumber] = abiCoder.decode(['uint256'], decodedResult); + expect(decodedNumber).to.equal(expectedNumber); }); it('should revert when calling state changing function -> setName(string)', async () => { @@ -483,8 +521,6 @@ export const shouldBehaveLikePermissionStaticCall = ( it('should allow to interact with 2nd allowed contract - getName()', async () => { const targetContract = allowedTargetContracts[1]; - const name = await targetContract.getName(); - const payload = context.universalProfile.interface.encodeFunctionData('execute', [ OPERATION_TYPES.STATICCALL, targetContract.address, @@ -494,15 +530,17 @@ export const shouldBehaveLikePermissionStaticCall = ( const result = await context.keyManager.connect(caller).callStatic.execute(payload); - const [decodedResult] = abiCoder.decode(['string'], result); - expect(decodedResult).to.equal(name); + const [decodedResult] = abiCoder.decode(['bytes'], result); + + const expectedName = await targetContract.getName(); + + const [decodedString] = abiCoder.decode(['string'], decodedResult); + expect(decodedString).to.equal(expectedName); }); it('should allow to interact with 2nd allowed contract - getNumber()', async () => { const targetContract = allowedTargetContracts[1]; - const number = await targetContract.getNumber(); - const payload = context.universalProfile.interface.encodeFunctionData('execute', [ OPERATION_TYPES.STATICCALL, targetContract.address, @@ -512,8 +550,12 @@ export const shouldBehaveLikePermissionStaticCall = ( const result = await context.keyManager.connect(caller).callStatic.execute(payload); - const [decodedResult] = abiCoder.decode(['uint256'], result); - expect(decodedResult).to.equal(number); + const [decodedResult] = abiCoder.decode(['bytes'], result); + + const expectedNumber = await targetContract.getNumber(); + + const [decodedNumber] = abiCoder.decode(['uint256'], decodedResult); + expect(decodedNumber).to.equal(expectedNumber); }); it('should revert when calling state changing function -> setName(string)', async () => { @@ -586,8 +628,6 @@ export const shouldBehaveLikePermissionStaticCall = ( it('should bypass allowed calls check + allow ton interact with a random contract', async () => { const randomContract = await new TargetContract__factory(context.accounts[0]).deploy(); - const name = await randomContract.getName(); - const payload = context.universalProfile.interface.encodeFunctionData('execute', [ OPERATION_TYPES.STATICCALL, randomContract.address, @@ -599,8 +639,12 @@ export const shouldBehaveLikePermissionStaticCall = ( .connect(addressWithSuperStaticCall) .callStatic.execute(payload); - const [decodedResult] = abiCoder.decode(['string'], result); - expect(decodedResult).to.equal(name); + const [decodedBytes] = abiCoder.decode(['bytes'], result); + + const name = await randomContract.getName(); + + const [decodedString] = abiCoder.decode(['string'], decodedBytes); + expect(decodedString).to.equal(name); }); it('should revert with error `ERC725X_MsgValueDisallowedInStaticCall` when `value` param is not 0', async () => { @@ -746,8 +790,12 @@ export const shouldBehaveLikePermissionStaticCall = ( .connect(addressCanMakeStaticCall) .callStatic.execute(executePayload); - const [decodedResult] = abiCoder.decode(['string'], result); - expect(decodedResult).to.equal(await targetContract.getName()); + const [decodedBytes] = abiCoder.decode(['bytes'], result); + + const expectedName = await targetContract.getName(); + + const [decodedString] = abiCoder.decode(['string'], decodedBytes); + expect(decodedString).to.equal(expectedName); }); it('should revert with error `ERC725X_MsgValueDisallowedInStaticCall` when `value` param is not 0', async () => { @@ -784,8 +832,12 @@ export const shouldBehaveLikePermissionStaticCall = ( .connect(addressCanMakeStaticCall) .callStatic.execute(executePayload); - const [decodedResult] = abiCoder.decode(['string'], result); - expect(decodedResult).to.equal(await targetContract.getName()); + const [decodedResult] = abiCoder.decode(['bytes'], result); + + const expectedName = await targetContract.getName(); + + const [decodedString] = abiCoder.decode(['string'], decodedResult); + expect(decodedString).to.equal(expectedName); }); it('should revert with error `ERC725X_MsgValueDisallowedInStaticCall` when `value` param is not 0', async () => { diff --git a/tests/LSP6KeyManager/Relay/ExecuteRelayCall.test.ts b/tests/LSP6KeyManager/Relay/ExecuteRelayCall.test.ts index a3c49da70..97bcc5ff1 100644 --- a/tests/LSP6KeyManager/Relay/ExecuteRelayCall.test.ts +++ b/tests/LSP6KeyManager/Relay/ExecuteRelayCall.test.ts @@ -25,6 +25,7 @@ import { // helpers import { + abiCoder, combineAllowedCalls, combinePermissions, createValidityTimestamps, @@ -107,6 +108,7 @@ export const shouldBehaveLikeExecuteRelayCall = ( await setupKeyManager(context, permissionKeys, permissionsValues); }); + describe('When signer does not have EXECUTE_RELAY_CALL permission', () => { it('should revert', async () => { const executeRelayCallPayload = context.universalProfile.interface.encodeFunctionData( @@ -165,6 +167,7 @@ export const shouldBehaveLikeExecuteRelayCall = ( .withArgs(signerWithoutExecuteRelayCall.address, 'EXECUTE_RELAY_CALL'); }); }); + describe('When testing signed message', () => { describe('When testing msg.value', () => { describe('When sending more than the signed msg.value', () => { @@ -1179,6 +1182,180 @@ export const shouldBehaveLikeExecuteRelayCall = ( }); }); }); + + describe('when calling `executeRelayCall -> LSP0.execute(uint256,address,uint256,bytes) -> TargetContract`', () => { + describe('when TargetContract returns an `uint256[]` array of 2 numbers', () => { + it('should return a `bytes` that can be decoded as a `uint256[]', async () => { + const targetContract = await new TargetContract__factory(context.accounts[0]).deploy(); + + const channelId = 0; + const validityTimestamp = 0; + + const keyManagerNonce = await context.keyManager.getNonce( + context.mainController.address, + channelId, + ); + + const getDynamicArrayOf2NumbersSig = targetContract.interface.getSighash( + 'getDynamicArrayOf2Numbers', + ); + + const erc725xExecutePayload = context.universalProfile.interface.encodeFunctionData( + 'execute', + [OPERATION_TYPES.STATICCALL, targetContract.address, 0, getDynamicArrayOf2NumbersSig], + ); + + const executeRelayCallSignature = await signLSP6ExecuteRelayCall( + context.keyManager, + keyManagerNonce.toHexString(), + validityTimestamp, + LOCAL_PRIVATE_KEYS.ACCOUNT0, + 0, + erc725xExecutePayload, + ); + + const result = await context.keyManager + .connect(context.mainController) + .callStatic.executeRelayCall( + executeRelayCallSignature, + keyManagerNonce, + validityTimestamp, + erc725xExecutePayload, + ); + + // Since we are calling the function `execute(uint256,address,uint256,bytes)` on the LSP0 contract + // and this function `returns(bytes memory)` + // we need to decode the result as `bytes` first before decoding to the expected type + // returned by the function targeted on the target contract + const [decodedResult] = abiCoder.decode(['bytes'], result); + + const expectedArrayOfNumbers = await targetContract.getDynamicArrayOf2Numbers(); + + const [decodedUint256Array] = abiCoder.decode(['uint256[]'], decodedResult); + expect(decodedUint256Array).to.deep.equal(expectedArrayOfNumbers); + }); + }); + }); + + describe('when calling `executeRelayCall -> LSP0.executeBatch(uint256[],address[],uint256[],bytes[])` and doing 2 x STATICCALLs in the batch', () => { + it('should return an array of `bytes[]` where each entry can be decoded individually', async () => { + const targetContract = await new TargetContract__factory(context.accounts[0]).deploy(); + + const channelId = 0; + const validityTimestamp = 0; + + const keyManagerNonce = await context.keyManager.getNonce( + context.mainController.address, + channelId, + ); + + const getNameSelector = targetContract.interface.getSighash('getName'); + const getNumberSelector = targetContract.interface.getSighash('getNumber'); + + const erc725xExecuteBatchPayload = context.universalProfile.interface.encodeFunctionData( + 'executeBatch', + [ + [OPERATION_TYPES.STATICCALL, OPERATION_TYPES.STATICCALL], + [targetContract.address, targetContract.address], + [0, 0], + [getNameSelector, getNumberSelector], + ], + ); + + const executeRelayCallSignature = await signLSP6ExecuteRelayCall( + context.keyManager, + keyManagerNonce.toHexString(), + validityTimestamp, + LOCAL_PRIVATE_KEYS.ACCOUNT0, + 0, + erc725xExecuteBatchPayload, + ); + + const result = await context.keyManager + .connect(context.mainController) + .callStatic.executeRelayCall( + executeRelayCallSignature, + keyManagerNonce, + validityTimestamp, + erc725xExecuteBatchPayload, + ); + + const expectedString = await targetContract.getName(); + const expectedNumber = await targetContract.getNumber(); + + // Since we are calling the function `executeBatch(uint256[],address[],uint256[],bytes[])` on the LSP0 contract + // and this function `returns(bytes[] memory)` + // we need to decode the result as `bytes[]` first before decoding each entry inside to the expected type + // returned by each functions called on the target contract + const [decodedBytesArray] = abiCoder.decode(['bytes[]'], result); + + const [decodedString] = abiCoder.decode(['string'], decodedBytesArray[0]); + expect(decodedString).to.equal(expectedString); + + const [decodedNumber] = abiCoder.decode(['uint256'], decodedBytesArray[1]); + expect(decodedNumber).to.equal(expectedNumber); + }); + }); + + describe('when calling `executeRelayCall -> LSP0.transferOwnership(address)`', () => { + it('should return nothing 0x, set the `pendingOwner` and emit `PermissionsVerified` event with right arguments', async () => { + const channelId = 0; + const validityTimestamp = 0; + + const keyManagerNonce = await context.keyManager.getNonce( + context.mainController.address, + channelId, + ); + + const newOwner = context.accounts[1].address; + + const transferOwnershipPayload = context.universalProfile.interface.encodeFunctionData( + 'transferOwnership', + [newOwner], + ); + + const executeRelayCallSignature = await signLSP6ExecuteRelayCall( + context.keyManager, + keyManagerNonce.toHexString(), + validityTimestamp, + LOCAL_PRIVATE_KEYS.ACCOUNT0, + 0, + transferOwnershipPayload, + ); + + const result = await context.keyManager + .connect(context.mainController) + .callStatic.executeRelayCall( + executeRelayCallSignature, + keyManagerNonce, + validityTimestamp, + transferOwnershipPayload, + ); + + // Since the function transferOwnership does not `returns` anything, the result should be 0x + expect(result).to.equal('0x'); + + // Run the transaction + const tx = await context.keyManager + .connect(context.mainController) + .executeRelayCall( + executeRelayCallSignature, + keyManagerNonce, + validityTimestamp, + transferOwnershipPayload, + ); + + // CHECK that the pendingOwner is set + expect(await context.universalProfile.pendingOwner()).to.equal(newOwner); + + // CHECK the `PermissionsVerified` event was emitted + await expect(tx).to.emit(context.keyManager, 'PermissionsVerified').withArgs( + context.mainController.address, // signer + 0, // value + context.universalProfile.interface.getSighash('transferOwnership'), // selector + ); + }); + }); }); }); @@ -1419,7 +1596,7 @@ export const shouldBehaveLikeExecuteRelayCall = ( describe('when specifying msg.value', () => { describe('when total `values[]` is LESS than `msg.value`', () => { - it('should revert because insufficent `msg.value`', async () => { + it('should revert because insufficient `msg.value`', async () => { const firstRecipient = context.accounts[1].address; const secondRecipient = context.accounts[2].address; const thirdRecipient = context.accounts[3].address; From 8f1fb959f0b9267bc099f774ca9a98fdd8921b97 Mon Sep 17 00:00:00 2001 From: Yamen Merhi Date: Thu, 2 Nov 2023 17:16:16 +0200 Subject: [PATCH 06/22] fix: [M-03] allow setting 21 bytes under LSP17Extension data key in LSP6KeyManager (#777) * fix: allow to set 20/21 bytes under LSP17Extension data key in LSP6KeyManager * test: add necessary tests * chore: run prettier * Update contracts/LSP6KeyManager/LSP6Modules/LSP6SetDataModule.sol Co-authored-by: Jean Cvllr <31145285+CJ42@users.noreply.github.com> * test: test error for different controller permissions --------- Co-authored-by: Jean Cvllr <31145285+CJ42@users.noreply.github.com> Co-authored-by: CJ42 --- .../LSP6Modules/LSP6SetDataModule.sol | 8 +- .../PermissionChangeAddExtensions.test.ts | 52 ++++ .../PermissionChangeAddExtensions.test.ts | 239 +++++++++++++++++- 3 files changed, 296 insertions(+), 3 deletions(-) diff --git a/contracts/LSP6KeyManager/LSP6Modules/LSP6SetDataModule.sol b/contracts/LSP6KeyManager/LSP6Modules/LSP6SetDataModule.sol index 03c43945f..9d7ae470f 100644 --- a/contracts/LSP6KeyManager/LSP6Modules/LSP6SetDataModule.sol +++ b/contracts/LSP6KeyManager/LSP6Modules/LSP6SetDataModule.sol @@ -321,8 +321,12 @@ abstract contract LSP6SetDataModule { // LSP17Extension: } else if (bytes12(inputDataKey) == _LSP17_EXTENSION_PREFIX) { - // CHECK that `dataValue` contains exactly 20 bytes, which corresponds to an address for a LSP17 Extension - if (inputDataValue.length != 20 && inputDataValue.length != 0) { + // CHECK that `dataValue` contains exactly 20 or 21 bytes (if setting to forward value), which corresponds to an address for a LSP17 Extension + if ( + inputDataValue.length != 20 && + inputDataValue.length != 21 && + inputDataValue.length != 0 + ) { revert InvalidDataValuesForDataKeys( inputDataKey, inputDataValue diff --git a/tests/LSP20CallVerification/LSP6/Admin/PermissionChangeAddExtensions.test.ts b/tests/LSP20CallVerification/LSP6/Admin/PermissionChangeAddExtensions.test.ts index 7b96c5875..40f050a36 100644 --- a/tests/LSP20CallVerification/LSP6/Admin/PermissionChangeAddExtensions.test.ts +++ b/tests/LSP20CallVerification/LSP6/Admin/PermissionChangeAddExtensions.test.ts @@ -962,5 +962,57 @@ export const shouldBehaveLikePermissionChangeOrAddExtensions = ( }); }); }); + + describe('when setting random bytes under the LSP17Extension data key ', () => { + it('should be allowed to set a 20 bytes long address', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const value = ethers.Wallet.createRandom().address.toLowerCase(); + + await context.universalProfile.connect(context.mainController).setData(key, value); + + const result = await context.universalProfile.getData(key); + expect(result).to.equal(value); + }); + + it('should be allowed to set a 21 bytes long address', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const value = ethers.Wallet.createRandom().address.toLowerCase() + '00'; + + await context.universalProfile.connect(context.mainController).setData(key, value); + + const result = await context.universalProfile.getData(key); + expect(result).to.equal(value); + }); + + it('should revert when setting a random 10 bytes value', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const randomValue = '0xcafecafecafecafecafe'; + + await expect( + context.universalProfile.connect(context.mainController).setData(key, randomValue), + ) + .to.be.revertedWithCustomError(context.keyManager, 'InvalidDataValuesForDataKeys') + .withArgs(key, randomValue); + }); + + it('should revert when setting a random 30 bytes value', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const randomValue = '0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef'; + + await expect( + context.universalProfile.connect(context.mainController).setData(key, randomValue), + ) + .to.be.revertedWithCustomError(context.keyManager, 'InvalidDataValuesForDataKeys') + .withArgs(key, randomValue); + }); + }); }); }; diff --git a/tests/LSP6KeyManager/Admin/PermissionChangeAddExtensions.test.ts b/tests/LSP6KeyManager/Admin/PermissionChangeAddExtensions.test.ts index 5cb9a7ab2..561c9b5db 100644 --- a/tests/LSP6KeyManager/Admin/PermissionChangeAddExtensions.test.ts +++ b/tests/LSP6KeyManager/Admin/PermissionChangeAddExtensions.test.ts @@ -274,6 +274,58 @@ export const shouldBehaveLikePermissionChangeOrAddExtensions = ( const result = await context.universalProfile.getDataBatch(payloadParam.dataKeys); expect(result).to.deep.equal(payloadParam.dataValues); }); + + describe('when setting random bytes under the LSP17Extension data key ', () => { + it('should be allowed to set a 20 bytes long address', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const value = ethers.Wallet.createRandom().address.toLowerCase(); + + await context.universalProfile.connect(context.mainController).setData(key, value); + + const result = await context.universalProfile.getData(key); + expect(result).to.equal(value); + }); + + it('should be allowed to set a 21 bytes long address', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const value = ethers.Wallet.createRandom().address.toLowerCase() + '00'; + + await context.universalProfile.connect(context.mainController).setData(key, value); + + const result = await context.universalProfile.getData(key); + expect(result).to.equal(value); + }); + + it('should revert when setting a random 10 bytes value', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const randomValue = '0xcafecafecafecafecafe'; + + await expect( + context.universalProfile.connect(context.mainController).setData(key, randomValue), + ) + .to.be.revertedWithCustomError(context.keyManager, 'InvalidDataValuesForDataKeys') + .withArgs(key, randomValue); + }); + + it('should revert when setting a random 30 bytes value', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const randomValue = '0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef'; + + await expect( + context.universalProfile.connect(context.mainController).setData(key, randomValue), + ) + .to.be.revertedWithCustomError(context.keyManager, 'InvalidDataValuesForDataKeys') + .withArgs(key, randomValue); + }); + }); }); describe('when caller is an address with ADD/CHANGE Extensions permission', () => { @@ -327,10 +379,62 @@ export const shouldBehaveLikePermissionChangeOrAddExtensions = ( const result = await context.universalProfile.getData(payloadParam.dataKey); expect(result).to.equal(payloadParam.dataValue); }); + + describe('when setting random bytes under the LSP17Extension data key ', () => { + it('should be allowed to set a 20 bytes long address', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const value = ethers.Wallet.createRandom().address.toLowerCase(); + + await context.universalProfile.connect(canAddAndChangeExtensions).setData(key, value); + + const result = await context.universalProfile.getData(key); + expect(result).to.equal(value); + }); + + it('should be allowed to set a 21 bytes long address', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const value = ethers.Wallet.createRandom().address.toLowerCase() + '00'; + + await context.universalProfile.connect(canAddAndChangeExtensions).setData(key, value); + + const result = await context.universalProfile.getData(key); + expect(result).to.equal(value); + }); + + it('should revert when setting a random 10 bytes value', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const randomValue = '0xcafecafecafecafecafe'; + + await expect( + context.universalProfile.connect(canAddAndChangeExtensions).setData(key, randomValue), + ) + .to.be.revertedWithCustomError(context.keyManager, 'InvalidDataValuesForDataKeys') + .withArgs(key, randomValue); + }); + + it('should revert when setting a random 30 bytes value', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const randomValue = '0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef'; + + await expect( + context.universalProfile.connect(canAddAndChangeExtensions).setData(key, randomValue), + ) + .to.be.revertedWithCustomError(context.keyManager, 'InvalidDataValuesForDataKeys') + .withArgs(key, randomValue); + }); + }); }); describe('when caller is an address with ADDExtensions permission', () => { - it('should be allowed to ADD a ExtensionHandler key', async () => { + it('should be allowed to ADD an ExtensionHandler key', async () => { const payloadParam = { dataKey: extensionHandlerKey5, dataValue: extensionA, @@ -347,6 +451,56 @@ export const shouldBehaveLikePermissionChangeOrAddExtensions = ( expect(result).to.equal(payloadParam.dataValue); }); + it('should be allowed to set a 20 bytes long address for a new handler', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const value = ethers.Wallet.createRandom().address.toLowerCase(); + + await context.universalProfile.connect(canOnlyAddExtensions).setData(key, value); + + const result = await context.universalProfile.getData(key); + expect(result).to.equal(value); + }); + + it('should be allowed to set a 21 bytes long address for a new handler', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const value = ethers.Wallet.createRandom().address.toLowerCase() + '00'; + + await context.universalProfile.connect(canOnlyAddExtensions).setData(key, value); + + const result = await context.universalProfile.getData(key); + expect(result).to.equal(value); + }); + + it('should revert with `InvalidDataValuesForDataKeys` when setting a random 10 bytes value for a new handler', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const randomValue = '0xcafecafecafecafecafe'; + + await expect( + context.universalProfile.connect(canOnlyAddExtensions).setData(key, randomValue), + ) + .to.be.revertedWithCustomError(context.keyManager, 'InvalidDataValuesForDataKeys') + .withArgs(key, randomValue); + }); + + it('should revert with `InvalidDataValuesForDataKeys` when setting a random 30 bytes value for a new handler', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const randomValue = '0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef'; + + await expect( + context.universalProfile.connect(canOnlyAddExtensions).setData(key, randomValue), + ) + .to.be.revertedWithCustomError(context.keyManager, 'InvalidDataValuesForDataKeys') + .withArgs(key, randomValue); + }); + it("should NOT be allowed to edit the ExtensionHandler key set even if it's setting existing data", async () => { const payloadParam = { dataKey: extensionHandlerKey5, @@ -394,6 +548,28 @@ export const shouldBehaveLikePermissionChangeOrAddExtensions = ( .to.be.revertedWithCustomError(context.keyManager, 'NotAuthorised') .withArgs(canOnlyAddExtensions.address, 'CHANGEEXTENSIONS'); }); + + it('should revert with `InvalidDataValuesForDataKeys` error when setting a random 10 bytes value for an existing handler', async () => { + const key = extensionHandlerKey5; + const randomValue = '0xcafecafecafecafecafe'; + + await expect( + context.universalProfile.connect(canOnlyAddExtensions).setData(key, randomValue), + ) + .to.be.revertedWithCustomError(context.keyManager, 'InvalidDataValuesForDataKeys') + .withArgs(key, randomValue); + }); + + it('should revert with `InvalidDataValuesForDataKeys` error when setting a random 30 bytes value for an existing handler', async () => { + const key = extensionHandlerKey5; + const randomValue = '0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef'; + + await expect( + context.universalProfile.connect(canOnlyAddExtensions).setData(key, randomValue), + ) + .to.be.revertedWithCustomError(context.keyManager, 'InvalidDataValuesForDataKeys') + .withArgs(key, randomValue); + }); }); describe('when caller is an address with CHANGEExtensions permission', () => { @@ -413,6 +589,64 @@ export const shouldBehaveLikePermissionChangeOrAddExtensions = ( .withArgs(canOnlyChangeExtensions.address, 'ADDEXTENSIONS'); }); + it('should NOT be allowed to set a 20 bytes long address for a new handler', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const value = ethers.Wallet.createRandom().address.toLowerCase(); + + const payload = context.universalProfile.interface.encodeFunctionData('setData', [ + key, + value, + ]); + + await expect(context.keyManager.connect(canOnlyChangeExtensions).execute(payload)) + .to.be.revertedWithCustomError(context.keyManager, 'NotAuthorised') + .withArgs(canOnlyChangeExtensions.address, 'ADDEXTENSIONS'); + }); + + it('should NOT be allowed to set a 21 bytes long address for a new handler', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const value = ethers.Wallet.createRandom().address.toLowerCase() + '00'; + + const payload = context.universalProfile.interface.encodeFunctionData('setData', [ + key, + value, + ]); + + await expect(context.keyManager.connect(canOnlyChangeExtensions).execute(payload)) + .to.be.revertedWithCustomError(context.keyManager, 'NotAuthorised') + .withArgs(canOnlyChangeExtensions.address, 'ADDEXTENSIONS'); + }); + + it('should revert with `InvalidValueForDataKey` error when setting a random 10 bytes value for a new handler', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const randomValue = '0xcafecafecafecafecafe'; + + await expect( + context.universalProfile.connect(canOnlyChangeExtensions).setData(key, randomValue), + ) + .to.be.revertedWithCustomError(context.keyManager, 'InvalidDataValuesForDataKeys') + .withArgs(key, randomValue); + }); + + it('should revert with `InvalidValueForDataKey` error when setting a random 30 bytes value for a new handler', async () => { + const key = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + ethers.utils.hexlify(ethers.utils.randomBytes(20)).substring(2); + const randomValue = '0xbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeefbeef'; + + await expect( + context.universalProfile.connect(canOnlyChangeExtensions).setData(key, randomValue), + ) + .to.be.revertedWithCustomError(context.keyManager, 'InvalidDataValuesForDataKeys') + .withArgs(key, randomValue); + }); + it('should be allowed to edit the ExtensionHandler key set', async () => { const payloadParam = { dataKey: extensionHandlerKey5, @@ -466,6 +700,7 @@ export const shouldBehaveLikePermissionChangeOrAddExtensions = ( await context.keyManager.connect(context.mainController).execute(payload); }); + it('should NOT be allowed to ADD another ExtensionHandler key', async () => { const payloadParam = { dataKey: extensionHandlerKey1, @@ -1029,6 +1264,7 @@ export const shouldBehaveLikePermissionChangeOrAddExtensions = ( .withArgs(canOnlySuperSetData.address, 'ADDEXTENSIONS'); }); }); + describe('when Adding multiple ExtensionHandler keys with adding ERC725Y Data Key', () => { it("should revert because caller don't have ADDExtensions permission", async () => { const payloadParam = { @@ -1108,6 +1344,7 @@ export const shouldBehaveLikePermissionChangeOrAddExtensions = ( await context.keyManager.connect(context.mainController).execute(payload); }); + describe('When adding ExtensionHandler key and one of his allowedERC725Y Data Key', () => { it('should pass', async () => { const payloadParam = { From d6356a712dd6358c2df695d06dc9c731e79f2ba5 Mon Sep 17 00:00:00 2001 From: Yamen Merhi Date: Thu, 2 Nov 2023 17:37:37 +0200 Subject: [PATCH 07/22] refactor: Prevent casting LSP17 Data shorter than 20 bytes (#778) * refactor: Prevent casting data > 20 bytes for LSP17Extensions * test: add necessary tests * chore: apply suggested changes --------- Co-authored-by: Jean Cvllr <31145285+CJ42@users.noreply.github.com> --- .../LSP0ERC725AccountCore.sol | 6 ++++ contracts/LSP9Vault/LSP9VaultCore.sol | 15 ++++++++-- .../LSP17Extendable.behaviour.ts | 29 +++++++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol index 7f6f7e593..385662607 100644 --- a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol +++ b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol @@ -838,6 +838,12 @@ abstract contract LSP0ERC725AccountCore is mappedExtensionDataKey ); + // Prevent casting data shorter than 20 bytes to an address to avoid + // unintentionally calling a different extension, return address(0) instead. + if (extensionData.length < 20) { + return (address(0), false); + } + // CHECK if the `extensionData` is 21 bytes long // - 20 bytes = extension's address // - 1 byte `0x01` as a boolean indicating if the contract should forward the value to the extension or not diff --git a/contracts/LSP9Vault/LSP9VaultCore.sol b/contracts/LSP9Vault/LSP9VaultCore.sol index 10205a790..a7c04e811 100644 --- a/contracts/LSP9Vault/LSP9VaultCore.sol +++ b/contracts/LSP9Vault/LSP9VaultCore.sol @@ -603,10 +603,19 @@ contract LSP9VaultCore is mappedExtensionDataKey ); - // Check if the extensionData is 21 bytes long (20 bytes of address + 1 byte as bool indicator ot forwards the value) + // Prevent casting data shorter than 20 bytes to an address to avoid + // unintentionally calling a different extension, return address(0) instead. + if (extensionData.length < 20) { + return (address(0), false); + } + + // CHECK if the `extensionData` is 21 bytes long + // - 20 bytes = extension's address + // - 1 byte `0x01` as a boolean indicating if the contract should forward the value to the extension or not if (extensionData.length == 21) { - // Check if the last byte is 1 (true) - if (extensionData[20] == hex"01") { + // If the last byte is set to `0x01` (`true`) + // this indicates that the contract should forward the value to the extension + if (extensionData[20] == 0x01) { // Return the address of the extension return (address(bytes20(extensionData)), true); } diff --git a/tests/LSP17ContractExtension/LSP17Extendable.behaviour.ts b/tests/LSP17ContractExtension/LSP17Extendable.behaviour.ts index 0f9710b30..184d9dfce 100644 --- a/tests/LSP17ContractExtension/LSP17Extendable.behaviour.ts +++ b/tests/LSP17ContractExtension/LSP17Extendable.behaviour.ts @@ -911,6 +911,35 @@ export const shouldBehaveLikeLSP17 = (buildContext: () => Promise { + describe('when setting less than 20 bytes as data value for the LSP17Extension data key', () => { + const randomSelector = ethers.utils.hexlify(ethers.utils.randomBytes(4)); + const randomBytes10Value = ethers.utils.hexlify(ethers.utils.randomBytes(10)); + + const lsp17DataKey = + ERC725YDataKeys.LSP17.LSP17ExtensionPrefix + + randomSelector.substring(2) + + '00'.repeat(16); + + it('should pass when setting the bytes', async () => { + await expect(context.contract.setData(lsp17DataKey, randomBytes10Value)) + .to.emit(context.contract, 'DataChanged') + .withArgs(lsp17DataKey, randomBytes10Value); + }); + + it('should revert with no ExtensionFoundForSelector when calling the function selector mapped to the 10 random bytes', async () => { + await expect( + context.accounts[0].sendTransaction({ + to: context.contract.address, + data: randomSelector, + }), + ) + .to.be.revertedWithCustomError(context.contract, 'NoExtensionFoundForFunctionSelector') + .withArgs(randomSelector); + }); + }); + }); + describe('use cases', async () => { describe('when interacting with a contract that require the recipient to implement onERC721Received function to mint', () => { let token: RequireCallbackToken; From e33477e290b1c9b1fba3deb6e10cf2f3df1a4fa6 Mon Sep 17 00:00:00 2001 From: Jean Cvllr <31145285+CJ42@users.noreply.github.com> Date: Thu, 2 Nov 2023 15:57:02 +0000 Subject: [PATCH 08/22] refactor: shorten LSP1Utils function to `notifyUniversalReceiver` (#779) --- .../LSP0ERC725AccountCore.sol | 10 ++++----- .../LSP14Ownable2Step/LSP14Ownable2Step.sol | 8 +++---- contracts/LSP1UniversalReceiver/LSP1Utils.sol | 2 +- .../LSP7DigitalAsset/LSP7DigitalAssetCore.sol | 21 ++++++------------- .../LSP8IdentifiableDigitalAssetCore.sol | 14 ++++--------- .../extensions/LSP8CompatibleERC721.sol | 5 +---- .../LSP8CompatibleERC721InitAbstract.sol | 5 +---- contracts/LSP9Vault/LSP9Vault.sol | 2 +- contracts/LSP9Vault/LSP9VaultCore.sol | 8 +++---- contracts/LSP9Vault/LSP9VaultInitAbstract.sol | 2 +- .../LSP1UniversalReceiver/LSP1Utils.md | 4 ++-- 11 files changed, 30 insertions(+), 51 deletions(-) diff --git a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol index 385662607..1a3d13fac 100644 --- a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol +++ b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol @@ -541,7 +541,7 @@ abstract contract LSP0ERC725AccountCore is emit OwnershipTransferStarted(currentOwner, pendingNewOwner); // notify the pending owner through LSP1 - pendingNewOwner.tryNotifyUniversalReceiver( + pendingNewOwner.notifyUniversalReceiver( _TYPEID_LSP0_OwnershipTransferStarted, "" ); @@ -561,7 +561,7 @@ abstract contract LSP0ERC725AccountCore is emit OwnershipTransferStarted(currentOwner, pendingNewOwner); // notify the pending owner through LSP1 - pendingNewOwner.tryNotifyUniversalReceiver( + pendingNewOwner.notifyUniversalReceiver( _TYPEID_LSP0_OwnershipTransferStarted, "" ); @@ -603,13 +603,13 @@ abstract contract LSP0ERC725AccountCore is } // notify the previous owner if supports LSP1 - previousOwner.tryNotifyUniversalReceiver( + previousOwner.notifyUniversalReceiver( _TYPEID_LSP0_OwnershipTransferred_SenderNotification, "" ); // notify the pending owner if supports LSP1 - pendingOwnerAddress.tryNotifyUniversalReceiver( + pendingOwnerAddress.notifyUniversalReceiver( _TYPEID_LSP0_OwnershipTransferred_RecipientNotification, "" ); @@ -649,7 +649,7 @@ abstract contract LSP0ERC725AccountCore is LSP14Ownable2Step._renounceOwnership(); if (owner() == address(0)) { - previousOwner.tryNotifyUniversalReceiver( + previousOwner.notifyUniversalReceiver( _TYPEID_LSP0_OwnershipTransferred_SenderNotification, "" ); diff --git a/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol b/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol index 124eb1a75..10e6bea5f 100644 --- a/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol +++ b/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol @@ -97,7 +97,7 @@ abstract contract LSP14Ownable2Step is ILSP14Ownable2Step, OwnableUnset { address currentOwner = owner(); emit OwnershipTransferStarted(currentOwner, newOwner); - newOwner.tryNotifyUniversalReceiver( + newOwner.notifyUniversalReceiver( _TYPEID_LSP14_OwnershipTransferStarted, "" ); @@ -116,12 +116,12 @@ abstract contract LSP14Ownable2Step is ILSP14Ownable2Step, OwnableUnset { _acceptOwnership(); - previousOwner.tryNotifyUniversalReceiver( + previousOwner.notifyUniversalReceiver( _TYPEID_LSP14_OwnershipTransferred_SenderNotification, "" ); - msg.sender.tryNotifyUniversalReceiver( + msg.sender.notifyUniversalReceiver( _TYPEID_LSP14_OwnershipTransferred_RecipientNotification, "" ); @@ -142,7 +142,7 @@ abstract contract LSP14Ownable2Step is ILSP14Ownable2Step, OwnableUnset { _renounceOwnership(); if (owner() == address(0)) { - previousOwner.tryNotifyUniversalReceiver( + previousOwner.notifyUniversalReceiver( _TYPEID_LSP14_OwnershipTransferred_SenderNotification, "" ); diff --git a/contracts/LSP1UniversalReceiver/LSP1Utils.sol b/contracts/LSP1UniversalReceiver/LSP1Utils.sol index 20f954851..026c3e8e9 100644 --- a/contracts/LSP1UniversalReceiver/LSP1Utils.sol +++ b/contracts/LSP1UniversalReceiver/LSP1Utils.sol @@ -35,7 +35,7 @@ library LSP1Utils { * @param typeId A `bytes32` typeId. * @param data Any optional data to send to the `universalReceiver` function to the `lsp1Implementation` address. */ - function tryNotifyUniversalReceiver( + function notifyUniversalReceiver( address lsp1Implementation, bytes32 typeId, bytes memory data diff --git a/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol b/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol index 671b0b4e8..621fc66a6 100644 --- a/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol +++ b/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol @@ -133,10 +133,7 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset, Version { operatorNotificationData ); - operator.tryNotifyUniversalReceiver( - _TYPEID_LSP7_TOKENOPERATOR, - lsp1Data - ); + operator.notifyUniversalReceiver(_TYPEID_LSP7_TOKENOPERATOR, lsp1Data); } /** @@ -162,7 +159,7 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset, Version { operatorNotificationData ); - operator.tryNotifyUniversalReceiver( + operator.notifyUniversalReceiver( _TYPEID_LSP7_TOKENOPERATOR, lsp1Data ); @@ -220,10 +217,7 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset, Version { operatorNotificationData ); - operator.tryNotifyUniversalReceiver( - _TYPEID_LSP7_TOKENOPERATOR, - lsp1Data - ); + operator.notifyUniversalReceiver(_TYPEID_LSP7_TOKENOPERATOR, lsp1Data); } /** @@ -257,10 +251,7 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset, Version { operatorNotificationData ); - operator.tryNotifyUniversalReceiver( - _TYPEID_LSP7_TOKENOPERATOR, - lsp1Data - ); + operator.notifyUniversalReceiver(_TYPEID_LSP7_TOKENOPERATOR, lsp1Data); } // --- Transfer functionality @@ -474,7 +465,7 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset, Version { _afterTokenTransfer(from, address(0), amount, data); bytes memory lsp1Data = abi.encode(from, address(0), amount, data); - from.tryNotifyUniversalReceiver(_TYPEID_LSP7_TOKENSSENDER, lsp1Data); + from.notifyUniversalReceiver(_TYPEID_LSP7_TOKENSSENDER, lsp1Data); } /** @@ -572,7 +563,7 @@ abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset, Version { bytes memory lsp1Data = abi.encode(from, to, amount, data); - from.tryNotifyUniversalReceiver(_TYPEID_LSP7_TOKENSSENDER, lsp1Data); + from.notifyUniversalReceiver(_TYPEID_LSP7_TOKENSSENDER, lsp1Data); _notifyTokenReceiver(to, force, lsp1Data); } diff --git a/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetCore.sol b/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetCore.sol index 0bc85d354..5443f6a14 100644 --- a/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetCore.sol +++ b/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetCore.sol @@ -160,10 +160,7 @@ abstract contract LSP8IdentifiableDigitalAssetCore is operatorNotificationData ); - operator.tryNotifyUniversalReceiver( - _TYPEID_LSP8_TOKENOPERATOR, - lsp1Data - ); + operator.notifyUniversalReceiver(_TYPEID_LSP8_TOKENOPERATOR, lsp1Data); } /** @@ -205,7 +202,7 @@ abstract contract LSP8IdentifiableDigitalAssetCore is operatorNotificationData ); - operator.tryNotifyUniversalReceiver( + operator.notifyUniversalReceiver( _TYPEID_LSP8_TOKENOPERATOR, lsp1Data ); @@ -467,10 +464,7 @@ abstract contract LSP8IdentifiableDigitalAssetCore is data ); - tokenOwner.tryNotifyUniversalReceiver( - _TYPEID_LSP8_TOKENSSENDER, - lsp1Data - ); + tokenOwner.notifyUniversalReceiver(_TYPEID_LSP8_TOKENSSENDER, lsp1Data); } /** @@ -536,7 +530,7 @@ abstract contract LSP8IdentifiableDigitalAssetCore is bytes memory lsp1Data = abi.encode(from, to, tokenId, data); - from.tryNotifyUniversalReceiver(_TYPEID_LSP8_TOKENSSENDER, lsp1Data); + from.notifyUniversalReceiver(_TYPEID_LSP8_TOKENSSENDER, lsp1Data); _notifyTokenReceiver(to, force, lsp1Data); } diff --git a/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.sol b/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.sol index 0d7e3a46e..5bb539fb7 100644 --- a/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.sol +++ b/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.sol @@ -357,10 +357,7 @@ abstract contract LSP8CompatibleERC721 is tokenId, operatorNotificationData ); - operator.tryNotifyUniversalReceiver( - _TYPEID_LSP8_TOKENOPERATOR, - lsp1Data - ); + operator.notifyUniversalReceiver(_TYPEID_LSP8_TOKENOPERATOR, lsp1Data); } /** diff --git a/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721InitAbstract.sol b/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721InitAbstract.sol index b48080620..1f57d45f2 100644 --- a/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721InitAbstract.sol +++ b/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721InitAbstract.sol @@ -366,10 +366,7 @@ abstract contract LSP8CompatibleERC721InitAbstract is tokenId, operatorNotificationData ); - operator.tryNotifyUniversalReceiver( - _TYPEID_LSP8_TOKENOPERATOR, - lsp1Data - ); + operator.notifyUniversalReceiver(_TYPEID_LSP8_TOKENOPERATOR, lsp1Data); } /** diff --git a/contracts/LSP9Vault/LSP9Vault.sol b/contracts/LSP9Vault/LSP9Vault.sol index cfe709014..4a547ab99 100644 --- a/contracts/LSP9Vault/LSP9Vault.sol +++ b/contracts/LSP9Vault/LSP9Vault.sol @@ -57,7 +57,7 @@ contract LSP9Vault is LSP9VaultCore { _LSP9_SUPPORTED_STANDARDS_VALUE ); - newOwner.tryNotifyUniversalReceiver( + newOwner.notifyUniversalReceiver( _TYPEID_LSP9_OwnershipTransferred_RecipientNotification, "" ); diff --git a/contracts/LSP9Vault/LSP9VaultCore.sol b/contracts/LSP9Vault/LSP9VaultCore.sol index a7c04e811..650fbc6a6 100644 --- a/contracts/LSP9Vault/LSP9VaultCore.sol +++ b/contracts/LSP9Vault/LSP9VaultCore.sol @@ -438,7 +438,7 @@ contract LSP9VaultCore is address currentOwner = owner(); emit OwnershipTransferStarted(currentOwner, newOwner); - newOwner.tryNotifyUniversalReceiver( + newOwner.notifyUniversalReceiver( _TYPEID_LSP9_OwnershipTransferStarted, "" ); @@ -460,12 +460,12 @@ contract LSP9VaultCore is _acceptOwnership(); - previousOwner.tryNotifyUniversalReceiver( + previousOwner.notifyUniversalReceiver( _TYPEID_LSP9_OwnershipTransferred_SenderNotification, "" ); - msg.sender.tryNotifyUniversalReceiver( + msg.sender.notifyUniversalReceiver( _TYPEID_LSP9_OwnershipTransferred_RecipientNotification, "" ); @@ -488,7 +488,7 @@ contract LSP9VaultCore is LSP14Ownable2Step._renounceOwnership(); if (owner() == address(0)) { - previousOwner.tryNotifyUniversalReceiver( + previousOwner.notifyUniversalReceiver( _TYPEID_LSP9_OwnershipTransferred_SenderNotification, "" ); diff --git a/contracts/LSP9Vault/LSP9VaultInitAbstract.sol b/contracts/LSP9Vault/LSP9VaultInitAbstract.sol index bcc988812..97f67ac1c 100644 --- a/contracts/LSP9Vault/LSP9VaultInitAbstract.sol +++ b/contracts/LSP9Vault/LSP9VaultInitAbstract.sol @@ -60,7 +60,7 @@ abstract contract LSP9VaultInitAbstract is Initializable, LSP9VaultCore { _LSP9_SUPPORTED_STANDARDS_VALUE ); - newOwner.tryNotifyUniversalReceiver( + newOwner.notifyUniversalReceiver( _TYPEID_LSP9_OwnershipTransferred_RecipientNotification, "" ); diff --git a/docs/libraries/LSP1UniversalReceiver/LSP1Utils.md b/docs/libraries/LSP1UniversalReceiver/LSP1Utils.md index c116ad6c8..a897fa7d0 100644 --- a/docs/libraries/LSP1UniversalReceiver/LSP1Utils.md +++ b/docs/libraries/LSP1UniversalReceiver/LSP1Utils.md @@ -24,10 +24,10 @@ Any method labeled as `internal` serves as utility function within the contract. Internal functions cannot be called externally, whether from other smart contracts, dApp interfaces, or backend services. Their restricted accessibility ensures that they remain exclusively available within the context of the current contract, promoting controlled and encapsulated usage of these internal utilities. -### tryNotifyUniversalReceiver +### notifyUniversalReceiver ```solidity -function tryNotifyUniversalReceiver( +function notifyUniversalReceiver( address lsp1Implementation, bytes32 typeId, bytes data From aa306377a36cadd95e91abd360da716393012117 Mon Sep 17 00:00:00 2001 From: Jean Cvllr <31145285+CJ42@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:12:49 +0000 Subject: [PATCH 09/22] refactor: use `modifier` to check if notifier is EOA or not (#780) --- .../LSP1UniversalReceiverDelegateUP.sol | 62 +++++++++---------- .../LSP1UniversalReceiverDelegateVault.sol | 39 +++++++----- 2 files changed, 53 insertions(+), 48 deletions(-) diff --git a/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol b/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol index 89b67a221..04eba69bc 100644 --- a/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol +++ b/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol @@ -60,6 +60,26 @@ contract LSP1UniversalReceiverDelegateUP is { using ERC165Checker for address; + /** + * @dev When receiving notifications about: + * - LSP7 Tokens sent or received + * - LSP8 Tokens sent or received + * - LSP9 Vaults sent or received + * The notifier should be either the LSP7 or LSP8 or LSP9 contract. + * + * We revert to avoid registering the EOA as asset (spam protection) + * if we received a typeId associated with tokens or vaults transfers. + * + * @param notifier The address that notified. + */ + modifier NotEOA(address notifier) { + // solhint-disable-next-line avoid-tx-origin + if (notifier == tx.origin) { + revert CannotRegisterEOAsAsAssets(notifier); + } + _; + } + /** * @dev * 1. Writes the data keys of the received [LSP-7-DigitalAsset], [LSP-8-IdentifiableDigitalAsset] and [LSP-9-Vault] contract addresses into the account storage according to the [LSP-5-ReceivedAssets] and [LSP-10-ReceivedVaults] Standard. @@ -123,14 +143,9 @@ contract LSP1UniversalReceiverDelegateUP is * * @param notifier The LSP7 or LSP8 token address. */ - function _tokenSender(address notifier) internal returns (bytes memory) { - // The notifier is supposed to be either the LSP7 or LSP8 or LSP9 contract - // If it's EOA we revert (spam protection) - // solhint-disable-next-line avoid-tx-origin - if (notifier == tx.origin) { - revert CannotRegisterEOAsAsAssets(notifier); - } - + function _tokenSender( + address notifier + ) internal NotEOA(notifier) returns (bytes memory) { // if the amount sent is not the full balance, then do not update the keys try ILSP7DigitalAsset(notifier).balanceOf(msg.sender) returns ( uint256 balance @@ -168,14 +183,7 @@ contract LSP1UniversalReceiverDelegateUP is function _tokenRecipient( address notifier, bytes4 interfaceId - ) internal returns (bytes memory) { - // The notifier is supposed to be either the LSP7 or LSP8 or LSP9 contract - // If it's EOA we revert to avoid registering the EOA as asset or vault (spam protection) - // solhint-disable-next-line avoid-tx-origin - if (notifier == tx.origin) { - revert CannotRegisterEOAsAsAssets(notifier); - } - + ) internal NotEOA(notifier) returns (bytes memory) { // CHECK balance only when the Token contract is already deployed, // not when tokens are being transferred on deployment through the `constructor` if (notifier.code.length != 0) { @@ -213,14 +221,9 @@ contract LSP1UniversalReceiverDelegateUP is * * @param notifier The LSP9 vault address. */ - function _vaultSender(address notifier) internal returns (bytes memory) { - // The notifier is supposed to be either the LSP7 or LSP8 or LSP9 contract - // If it's EOA we revert (spam protection) - // solhint-disable-next-line avoid-tx-origin - if (notifier == tx.origin) { - revert CannotRegisterEOAsAsAssets(notifier); - } - + function _vaultSender( + address notifier + ) internal NotEOA(notifier) returns (bytes memory) { (bytes32[] memory dataKeys, bytes[] memory dataValues) = LSP10Utils .generateSentVaultKeys(msg.sender, notifier); @@ -243,14 +246,9 @@ contract LSP1UniversalReceiverDelegateUP is * * @param notifier The LSP9 vault address. */ - function _vaultRecipient(address notifier) internal returns (bytes memory) { - // The notifier is supposed to be either the LSP7 or LSP8 or LSP9 contract - // If it's EOA we revert to avoid registering the EOA as asset or vault (spam protection) - // solhint-disable-next-line avoid-tx-origin - if (notifier == tx.origin) { - revert CannotRegisterEOAsAsAssets(notifier); - } - + function _vaultRecipient( + address notifier + ) internal NotEOA(notifier) returns (bytes memory) { (bytes32[] memory dataKeys, bytes[] memory dataValues) = LSP10Utils .generateReceivedVaultKeys(msg.sender, notifier); diff --git a/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol b/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol index 994ec5347..d015df45f 100644 --- a/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol +++ b/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol @@ -48,6 +48,25 @@ contract LSP1UniversalReceiverDelegateVault is Version, ILSP1UniversalReceiverDelegate { + /** + * @dev When receiving notifications about: + * - LSP7 Tokens sent or received + * - LSP8 Tokens sent or received + * The notifier should be either the LSP7 or LSP8 contract. + * + * We revert to avoid registering the EOA as asset (spam protection) + * if we received a typeId associated with tokens transfers. + * + * @param notifier The address that notified. + */ + modifier NotEOA(address notifier) { + // solhint-disable-next-line avoid-tx-origin + if (notifier == tx.origin) { + revert CannotRegisterEOAsAsAssets(notifier); + } + _; + } + /** * @inheritdoc ILSP1UniversalReceiverDelegate * @dev Handles two cases: @@ -99,14 +118,9 @@ contract LSP1UniversalReceiverDelegateVault is * * @param notifier The LSP7 or LSP8 token address. */ - function _tokenSender(address notifier) internal returns (bytes memory) { - // The notifier is supposed to be either the LSP7 or LSP8 contract - // If it's EOA we revert to avoid registering the EOA as asset (spam protection) - // solhint-disable-next-line avoid-tx-origin - if (notifier == tx.origin) { - revert CannotRegisterEOAsAsAssets(notifier); - } - + function _tokenSender( + address notifier + ) internal NotEOA(notifier) returns (bytes memory) { // if the amount sent is not the full balance, then do not update the keys try ILSP7DigitalAsset(notifier).balanceOf(msg.sender) returns ( uint256 balance @@ -144,14 +158,7 @@ contract LSP1UniversalReceiverDelegateVault is function _tokenRecipient( address notifier, bytes4 interfaceId - ) internal returns (bytes memory) { - // The notifier is supposed to be either the LSP7 or LSP8 contract - // If it's EOA we revert to avoid registering the EOA as asset (spam protection) - // solhint-disable-next-line avoid-tx-origin - if (notifier == tx.origin) { - revert CannotRegisterEOAsAsAssets(notifier); - } - + ) internal NotEOA(notifier) returns (bytes memory) { // CHECK balance only when the Token contract is already deployed, // not when tokens are being transferred on deployment through the `constructor` if (notifier.code.length != 0) { From 5d8a7af60dc548730688c0895eeb27d402ec5cc5 Mon Sep 17 00:00:00 2001 From: Yamen Merhi Date: Thu, 2 Nov 2023 18:53:44 +0200 Subject: [PATCH 10/22] fix: [M-02] reset `_renounceOwnershipStartedAt` on `acceptOwnership(..)` in LSP14 (#775) * fix: reset `_renounceOwnershipStartedAt` on acceptOwnership(..) * test: add foundry test * chore: run format on foundry test * chore: add suggested changes --------- Co-authored-by: Jean Cvllr <31145285+CJ42@users.noreply.github.com> --- .../LSP14Ownable2Step/LSP14Ownable2Step.sol | 1 + .../AcceptOwnershipCleanState.sol | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 tests/foundry/LSP14Ownable2Step/AcceptOwnershipCleanState.sol diff --git a/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol b/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol index 10e6bea5f..84d7bd3e0 100644 --- a/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol +++ b/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol @@ -175,6 +175,7 @@ abstract contract LSP14Ownable2Step is ILSP14Ownable2Step, OwnableUnset { _setOwner(msg.sender); delete _pendingOwner; + delete _renounceOwnershipStartedAt; } /** diff --git a/tests/foundry/LSP14Ownable2Step/AcceptOwnershipCleanState.sol b/tests/foundry/LSP14Ownable2Step/AcceptOwnershipCleanState.sol new file mode 100644 index 000000000..8e1fe7f0c --- /dev/null +++ b/tests/foundry/LSP14Ownable2Step/AcceptOwnershipCleanState.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "@erc725/smart-contracts/contracts/constants.sol"; +import "../../../contracts/LSP0ERC725Account/LSP0ERC725Account.sol"; + +import { + LSP20EOACannotVerifyCall +} from "../../../contracts/LSP20CallVerification/LSP20Errors.sol"; + +contract LSP0Implementation is LSP0ERC725Account { + constructor(address _addr) LSP0ERC725Account(_addr) {} + + function renounceOwnershipStartedAt() public view returns (uint256) { + return _renounceOwnershipStartedAt; + } +} + +contract LSP0StorageUpdater { + // _renounceOwnershipStartedAt is at slot 2 for LSP0ERC725Account + bytes32[2] __gap; + uint256 _renounceOwnershipStartedAt; + + function setRenounceOwnershipStartedAt( + uint256 newRenounceOwnershipStartedAt + ) external { + _renounceOwnershipStartedAt = newRenounceOwnershipStartedAt; + } +} + +contract OwnershipAccepter { + function acceptOwnership(address _account) public { + LSP0Implementation(payable(_account)).acceptOwnership(); + } +} + +contract TwoStepRenounceOwnershipTest is Test { + LSP0Implementation account; + OwnershipAccepter ownershipAccepter; + + function setUp() public { + // Deploy LSP0 account with this address as owner + account = new LSP0Implementation(address(this)); + ownershipAccepter = new OwnershipAccepter(); + } + + function testRenounceOwnershipVariableClearedAfterAcceptOwnership() public { + // Call transferOwnership so we can check acceptOwnership behavior + account.transferOwnership(address(ownershipAccepter)); + + // Overwrite _renounceOwnershipAt using a delegatecall + LSP0StorageUpdater implementation = new LSP0StorageUpdater(); + + uint256 newRenounceOwnershipStartedAt = 10_000_000; // number of blocks + + account.execute( + OPERATION_4_DELEGATECALL, + address(implementation), + 0, + abi.encodeWithSelector( + LSP0StorageUpdater.setRenounceOwnershipStartedAt.selector, + newRenounceOwnershipStartedAt + ) + ); + + // _renounceOwnershipAt is now set to this value + assertEq( + account.renounceOwnershipStartedAt(), + newRenounceOwnershipStartedAt + ); + + // Calling LSP0's `acceptOwnership()` function that + // should reset _renounceOwnershipAt variable + ownershipAccepter.acceptOwnership(address(account)); + + // Make sure the _renounceOwnershipAt is reset after acceptOwnership call + assertEq(account.renounceOwnershipStartedAt(), 0); + } +} From 63c06d0dfbc17abc40377ffbe14569241b0c13d5 Mon Sep 17 00:00:00 2001 From: "b00ste.lyx" <62855857+b00ste@users.noreply.github.com> Date: Thu, 2 Nov 2023 19:09:36 +0200 Subject: [PATCH 11/22] refactor: [M-05] notify owner via LSP1 when confirming `renounceOwnership` (#783) * refactor: notify when owner uses `renounceOwnership` * test: add tests for `renounceOwnership()` LSP1 hook --- .../LSP0ERC725AccountCore.sol | 42 +++++++----- contracts/Mocks/LSP20Owners/OwnerWIthURD.sol | 68 +++++++++++++++++++ .../LSP20CallVerification.behaviour.ts | 44 +++++++++++- tests/UniversalProfile.behaviour.ts | 44 +++++++++++- 4 files changed, 179 insertions(+), 19 deletions(-) create mode 100644 contracts/Mocks/LSP20Owners/OwnerWIthURD.sol diff --git a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol index 1a3d13fac..848675f96 100644 --- a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol +++ b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol @@ -638,27 +638,35 @@ abstract contract LSP0ERC725AccountCore is // If the caller is the owner perform renounceOwnership directly if (msg.sender == accountOwner) { - return LSP14Ownable2Step._renounceOwnership(); - } + address previousOwner = owner(); + LSP14Ownable2Step._renounceOwnership(); - // If the caller is not the owner, call {lsp20VerifyCall} on the owner - // Depending on the returnedStatus, a second call is done after transferring ownership - bool verifyAfter = _verifyCall(accountOwner); + if (owner() == address(0)) { + previousOwner.notifyUniversalReceiver( + _TYPEID_LSP0_OwnershipTransferred_SenderNotification, + "" + ); + } + } else { + // If the caller is not the owner, call {lsp20VerifyCall} on the owner + // Depending on the returnedStatus, a second call is done after transferring ownership + bool verifyAfter = _verifyCall(accountOwner); - address previousOwner = owner(); - LSP14Ownable2Step._renounceOwnership(); + address previousOwner = owner(); + LSP14Ownable2Step._renounceOwnership(); - if (owner() == address(0)) { - previousOwner.notifyUniversalReceiver( - _TYPEID_LSP0_OwnershipTransferred_SenderNotification, - "" - ); - } + if (owner() == address(0)) { + previousOwner.notifyUniversalReceiver( + _TYPEID_LSP0_OwnershipTransferred_SenderNotification, + "" + ); + } - // If verifyAfter is true, Call {lsp20VerifyCallResult} on the owner - // The transferOwnership function does not return, second parameter of {_verifyCallResult} will be empty - if (verifyAfter) { - _verifyCallResult(accountOwner, ""); + // If verifyAfter is true, Call {lsp20VerifyCallResult} on the owner + // The transferOwnership function does not return, second parameter of {_verifyCallResult} will be empty + if (verifyAfter) { + _verifyCallResult(accountOwner, ""); + } } } diff --git a/contracts/Mocks/LSP20Owners/OwnerWIthURD.sol b/contracts/Mocks/LSP20Owners/OwnerWIthURD.sol new file mode 100644 index 000000000..f95800550 --- /dev/null +++ b/contracts/Mocks/LSP20Owners/OwnerWIthURD.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.4; + +import { + ILSP1UniversalReceiver +} from "../../LSP1UniversalReceiver/ILSP1UniversalReceiver.sol"; + +import { + ILSP14Ownable2Step +} from "../../LSP14Ownable2Step/ILSP14Ownable2Step.sol"; + +import { + ILSP20CallVerifier +} from "../../LSP20CallVerification/ILSP20CallVerifier.sol"; +import { + _LSP20_VERIFY_CALL_SUCCESS_VALUE_WITH_POST_VERIFICATION, + _LSP20_VERIFY_CALL_RESULT_SUCCESS_VALUE +} from "../../LSP20CallVerification/LSP20Constants.sol"; + +contract OwnerWithURD is ILSP20CallVerifier, ILSP1UniversalReceiver { + address private immutable _OWNED_CONTRACT; + + constructor(address ownedContract) { + _OWNED_CONTRACT = ownedContract; + } + + function renounceOwnership() public { + ILSP14Ownable2Step(_OWNED_CONTRACT).renounceOwnership(); + } + + function acceptOwnership() public { + ILSP14Ownable2Step(_OWNED_CONTRACT).acceptOwnership(); + } + + function lsp20VerifyCall( + address, + address, + address, + uint256, + bytes memory + ) public pure returns (bytes4 returnedStatus) { + return _LSP20_VERIFY_CALL_SUCCESS_VALUE_WITH_POST_VERIFICATION; + } + + function lsp20VerifyCallResult( + bytes32, + bytes memory + ) external pure returns (bytes4) { + return _LSP20_VERIFY_CALL_RESULT_SUCCESS_VALUE; + } + + function supportsInterface(bytes4 interfaceId) public pure returns (bool) { + return interfaceId == type(ILSP1UniversalReceiver).interfaceId; + } + + function universalReceiver( + bytes32 typeId, + bytes memory receivedData + ) public payable virtual override returns (bytes memory returnedValues) { + emit UniversalReceiver( + msg.sender, + msg.value, + typeId, + receivedData, + returnedValues + ); + } +} diff --git a/tests/LSP20CallVerification/LSP20CallVerification.behaviour.ts b/tests/LSP20CallVerification/LSP20CallVerification.behaviour.ts index 27223c2bc..3298975a8 100644 --- a/tests/LSP20CallVerification/LSP20CallVerification.behaviour.ts +++ b/tests/LSP20CallVerification/LSP20CallVerification.behaviour.ts @@ -20,10 +20,12 @@ import { LSP0ERC725Account, ILSP20CallVerifier, ILSP20CallVerifier__factory, + OwnerWithURD, + OwnerWithURD__factory, } from '../../types'; // constants -import { LSP20_SUCCESS_VALUES, OPERATION_TYPES } from '../../constants'; +import { LSP1_TYPE_IDS, LSP20_SUCCESS_VALUES, OPERATION_TYPES } from '../../constants'; export type LSP20TestContext = { accounts: SignerWithAddress[]; @@ -200,6 +202,46 @@ export const shouldBehaveLikeLSP20 = (buildContext: () => Promise { + let newContractOwner: OwnerWithURD; + + before('Use custom owner that implements LSP1', async () => { + newContractOwner = await new OwnerWithURD__factory(context.accounts[0]).deploy( + context.universalProfile.address, + ); + + await context.universalProfile + .connect(context.deployParams.owner) + .transferOwnership(newContractOwner.address); + + await newContractOwner.acceptOwnership(); + }); + + after('`renounceOwnership()` was used, build new context', async () => { + context = await buildContext(); + }); + + it('should renounce ownership of the contract and call the URD of the previous owner', async () => { + await context.universalProfile.connect(context.accounts[0]).renounceOwnership(); + + await network.provider.send('hardhat_mine', [ethers.utils.hexValue(199)]); + + const tx = await context.universalProfile + .connect(context.accounts[0]) + .renounceOwnership(); + + await expect(tx) + .to.emit(newContractOwner, 'UniversalReceiver') + .withArgs( + context.universalProfile.address, + 0, + LSP1_TYPE_IDS.LSP0OwnershipTransferred_SenderNotification, + '0x', + '0x', + ); + }); + }); }); }); diff --git a/tests/UniversalProfile.behaviour.ts b/tests/UniversalProfile.behaviour.ts index b222c73af..d1eb2f81e 100644 --- a/tests/UniversalProfile.behaviour.ts +++ b/tests/UniversalProfile.behaviour.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { ethers } from 'hardhat'; +import { ethers, network } from 'hardhat'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; // types @@ -11,6 +11,8 @@ import { UniversalReceiverDelegateDataLYX, EmitEventExtension, EmitEventExtension__factory, + OwnerWithURD__factory, + OwnerWithURD, } from '../types'; // helpers @@ -736,6 +738,46 @@ export const shouldBehaveLikeLSP3 = ( }); }); }); + + describe('when using `renounceOwnership()`', () => { + describe('when caller is owner', () => { + let newContractOwner: OwnerWithURD; + + before('Use custom owner that implements LSP1', async () => { + newContractOwner = await new OwnerWithURD__factory(context.accounts[0]).deploy( + context.universalProfile.address, + ); + + await context.universalProfile + .connect(context.deployParams.owner) + .transferOwnership(newContractOwner.address); + + await newContractOwner.acceptOwnership(); + }); + + after('`renounceOwnership()` was used, build new context', async () => { + context = await buildContext(); + }); + + it('should renounce ownership of the contract and call the URD of the previous owner', async () => { + await newContractOwner.connect(context.accounts[0]).renounceOwnership(); + + await network.provider.send('hardhat_mine', [ethers.utils.hexValue(199)]); + + const tx = await newContractOwner.connect(context.accounts[0]).renounceOwnership(); + + await expect(tx) + .to.emit(newContractOwner, 'UniversalReceiver') + .withArgs( + context.universalProfile.address, + 0, + LSP1_TYPE_IDS.LSP0OwnershipTransferred_SenderNotification, + '0x', + '0x', + ); + }); + }); + }); }; export const shouldInitializeLikeLSP3 = (buildContext: () => Promise) => { From 78bb1e7e32ae8c6ce1decdbfa26fafa72c4c5387 Mon Sep 17 00:00:00 2001 From: "b00ste.lyx" <62855857+b00ste@users.noreply.github.com> Date: Fri, 3 Nov 2023 14:31:42 +0200 Subject: [PATCH 12/22] fix: [M-04 + L-02] improve empty call type checks for selector `0x00000000` (#776) * refactor: improve empty call check * test: add extra sanity tests for permission checks on value transfer * test: add Foundry tests for checking callTypes verification * refactor: check for callTypes on empty calls * test: add Foundry fuzzing tests for calltypes check against empty calls * test: add more Foundry tests to check against allowing `bytes4(0)` graffiti calls * docs: update LSP6 docs for internal function --------- Co-authored-by: CJ42 --- .../LSP6Modules/LSP6ExecuteModule.sol | 30 +- .../KeyManager/KeyManagerInternalsTester.sol | 4 +- .../LSP6KeyManager/LSP6KeyManager.md | 6 +- foundry.toml | 1 + .../PermissionTransferValue.test.ts | 215 +++++++++-- .../LSP6KeyManager/LSP6AllowedCallsTest.t.sol | 336 ++++++++++++++++++ 6 files changed, 538 insertions(+), 54 deletions(-) create mode 100644 tests/foundry/LSP6KeyManager/LSP6AllowedCallsTest.t.sol diff --git a/contracts/LSP6KeyManager/LSP6Modules/LSP6ExecuteModule.sol b/contracts/LSP6KeyManager/LSP6Modules/LSP6ExecuteModule.sol index f3aab5d24..04d5d90ba 100644 --- a/contracts/LSP6KeyManager/LSP6Modules/LSP6ExecuteModule.sol +++ b/contracts/LSP6KeyManager/LSP6Modules/LSP6ExecuteModule.sol @@ -250,16 +250,7 @@ abstract contract LSP6ExecuteModule { revert NoCallsAllowed(controllerAddress); } - bool isEmptyCall = data.length == 0; - - // CHECK if there is at least a 4 bytes function selector - bytes4 selector = data.length >= 4 ? bytes4(data) : bytes4(0); - - bytes4 requiredCallTypes = _extractCallType( - operationType, - value, - isEmptyCall - ); + bytes4 requiredCallTypes = _extractCallType(operationType, value, data); for (uint256 ii; ii < allowedCalls.length; ii += 34) { /// @dev structure of an AllowedCall @@ -294,11 +285,11 @@ abstract contract LSP6ExecuteModule { _isAllowedCallType(allowedCall, requiredCallTypes) && _isAllowedAddress(allowedCall, to) && _isAllowedStandard(allowedCall, to) && - _isAllowedFunction(allowedCall, selector) + _isAllowedFunction(allowedCall, data) ) return; } - revert NotAllowedCall(controllerAddress, to, selector); + revert NotAllowedCall(controllerAddress, to, bytes4(data)); } /** @@ -309,7 +300,7 @@ abstract contract LSP6ExecuteModule { function _extractCallType( uint256 operationType, uint256 value, - bool isEmptyCall + bytes memory data ) internal pure returns (bytes4 requiredCallTypes) { // if there is value being transferred, add the extra bit // for the first bit for Value Transfer in the `requiredCallTypes` @@ -317,7 +308,12 @@ abstract contract LSP6ExecuteModule { requiredCallTypes |= _ALLOWEDCALLS_TRANSFERVALUE; } - if (!isEmptyCall) { + bool isCallDataPresent = data.length != 0; + bool isEmptyCallWithoutValue = !isCallDataPresent && value == 0; + + // if we are doing a message call with some data + // or if we are doing an empty call without value + if (isCallDataPresent || isEmptyCallWithoutValue) { if (operationType == OPERATION_0_CALL) { requiredCallTypes |= _ALLOWEDCALLS_CALL; } else if (operationType == OPERATION_3_STATICCALL) { @@ -363,7 +359,7 @@ abstract contract LSP6ExecuteModule { function _isAllowedFunction( bytes memory allowedCall, - bytes4 requiredFunction + bytes memory data ) internal pure virtual returns (bool) { // = 28 bytes x 8 bits = 224 bits // @@ -372,7 +368,9 @@ abstract contract LSP6ExecuteModule { // 0000000ncafecafecafecafecafecafecafecafecafecafe5a5a5a5af1f1f1f1 bytes4 allowedFunction = bytes4(bytes32(allowedCall) << 224); - bool isFunctionCall = requiredFunction != bytes4(0); + bool isFunctionCall = data.length >= 4; + + bytes4 requiredFunction = bytes4(data); // ANY function = 0xffffffff return diff --git a/contracts/Mocks/KeyManager/KeyManagerInternalsTester.sol b/contracts/Mocks/KeyManager/KeyManagerInternalsTester.sol index 12facc51f..a6edba89f 100644 --- a/contracts/Mocks/KeyManager/KeyManagerInternalsTester.sol +++ b/contracts/Mocks/KeyManager/KeyManagerInternalsTester.sol @@ -33,7 +33,7 @@ contract KeyManagerInternalTester is LSP6KeyManager { } function verifyAllowedCall( - address _sender, + address controller, uint256 operationType, address to, uint256 value, @@ -41,7 +41,7 @@ contract KeyManagerInternalTester is LSP6KeyManager { ) public view { super._verifyAllowedCall( _target, - _sender, + controller, operationType, to, value, diff --git a/docs/contracts/LSP6KeyManager/LSP6KeyManager.md b/docs/contracts/LSP6KeyManager/LSP6KeyManager.md index 5c55131a7..e5dd80fec 100644 --- a/docs/contracts/LSP6KeyManager/LSP6KeyManager.md +++ b/docs/contracts/LSP6KeyManager/LSP6KeyManager.md @@ -906,7 +906,7 @@ function _verifyAllowedCall( function _extractCallType( uint256 operationType, uint256 value, - bool isEmptyCall + bytes data ) internal pure returns (bytes4 requiredCallTypes); ``` @@ -918,7 +918,7 @@ extract the bytes4 representation of a single bit for the type of call according | --------------- | :-------: | -------------------------------------------- | | `operationType` | `uint256` | 0 = CALL, 3 = STATICCALL or 3 = DELEGATECALL | | `value` | `uint256` | - | -| `isEmptyCall` | `bool` | - | +| `data` | `bytes` | - | #### Returns @@ -955,7 +955,7 @@ function _isAllowedStandard( ```solidity function _isAllowedFunction( bytes allowedCall, - bytes4 requiredFunction + bytes data ) internal pure returns (bool); ``` diff --git a/foundry.toml b/foundry.toml index 7d758faf7..a10f28357 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,3 +8,4 @@ test = 'tests/foundry' solc_version = "0.8.17" [fuzz] runs = 10_000 +max_test_rejects = 200_000 diff --git a/tests/LSP6KeyManager/Interactions/PermissionTransferValue.test.ts b/tests/LSP6KeyManager/Interactions/PermissionTransferValue.test.ts index c59d65538..4a0e2d0e6 100644 --- a/tests/LSP6KeyManager/Interactions/PermissionTransferValue.test.ts +++ b/tests/LSP6KeyManager/Interactions/PermissionTransferValue.test.ts @@ -52,10 +52,10 @@ export const shouldBehaveLikePermissionTransferValue = ( describe('when caller = EOA', () => { let canTransferValue: SignerWithAddress, canTransferValueAndCall: SignerWithAddress, - cannotTransferValue: SignerWithAddress; + canCallOnly: SignerWithAddress, + canNeitherCallNorTransferValue: SignerWithAddress; let recipient; - let recipientUP: UniversalProfile; // used to test when sending data as graffiti @@ -66,9 +66,10 @@ export const shouldBehaveLikePermissionTransferValue = ( canTransferValue = context.accounts[1]; canTransferValueAndCall = context.accounts[2]; - cannotTransferValue = context.accounts[3]; - recipient = context.accounts[4]; + canCallOnly = context.accounts[3]; + canNeitherCallNorTransferValue = context.accounts[4]; + recipient = context.accounts[5]; recipientUP = await new UniversalProfile__factory(context.accounts[0]).deploy( context.accounts[0].address, ); @@ -77,8 +78,8 @@ export const shouldBehaveLikePermissionTransferValue = ( const lsp17ExtensionDataKeyForGraffiti = ERC725YDataKeys.LSP17['LSP17ExtensionPrefix'] + - '00000000' + // selector for graffiti data, - '00000000000000000000000000000000'; // zero padded + '00'.repeat(4) + // bytes4 selector for graffiti data, + '00'.repeat(16); // zero padded on the right await recipientUP .connect(context.accounts[0]) @@ -90,23 +91,28 @@ export const shouldBehaveLikePermissionTransferValue = ( ethers.utils.getAddress(await recipientUP.getData(lsp17ExtensionDataKeyForGraffiti)), ).to.equal(graffitiExtension.address); + // prettier-ignore const permissionsKeys = [ - ERC725YDataKeys.LSP6['AddressPermissions:Permissions'] + - context.mainController.address.substring(2), - ERC725YDataKeys.LSP6['AddressPermissions:Permissions'] + - canTransferValue.address.substring(2), - ERC725YDataKeys.LSP6['AddressPermissions:AllowedCalls'] + - canTransferValue.address.substring(2), - ERC725YDataKeys.LSP6['AddressPermissions:Permissions'] + - canTransferValueAndCall.address.substring(2), - ERC725YDataKeys.LSP6['AddressPermissions:AllowedCalls'] + - canTransferValueAndCall.address.substring(2), - ERC725YDataKeys.LSP6['AddressPermissions:Permissions'] + - cannotTransferValue.address.substring(2), + // main controller + ERC725YDataKeys.LSP6['AddressPermissions:Permissions'] + context.mainController.address.substring(2), + // canTransferValue + ERC725YDataKeys.LSP6['AddressPermissions:Permissions'] + canTransferValue.address.substring(2), + ERC725YDataKeys.LSP6['AddressPermissions:AllowedCalls'] + canTransferValue.address.substring(2), + // canTransferValueAndCall + ERC725YDataKeys.LSP6['AddressPermissions:Permissions'] + canTransferValueAndCall.address.substring(2), + ERC725YDataKeys.LSP6['AddressPermissions:AllowedCalls'] + canTransferValueAndCall.address.substring(2), + // canCallOnly (not Transfer Value) + ERC725YDataKeys.LSP6['AddressPermissions:Permissions'] + canCallOnly.address.substring(2), + ERC725YDataKeys.LSP6['AddressPermissions:AllowedCalls'] + canCallOnly.address.substring(2), + // canNeitherCallNorTransferValue + ERC725YDataKeys.LSP6['AddressPermissions:Permissions'] + canNeitherCallNorTransferValue.address.substring(2), + ERC725YDataKeys.LSP6['AddressPermissions:AllowedCalls'] + canNeitherCallNorTransferValue.address.substring(2), ]; const permissionsValues = [ + // main controller ALL_PERMISSIONS, + // canTransferValue PERMISSIONS.TRANSFERVALUE, combineAllowedCalls( [CALLTYPE.VALUE, CALLTYPE.VALUE], @@ -114,6 +120,7 @@ export const shouldBehaveLikePermissionTransferValue = ( ['0xffffffff', '0xffffffff'], ['0xffffffff', '0xffffffff'], ), + // canTransferValueAndCall combinePermissions(PERMISSIONS.TRANSFERVALUE, PERMISSIONS.CALL), combineAllowedCalls( [ @@ -124,7 +131,26 @@ export const shouldBehaveLikePermissionTransferValue = ( ['0xffffffff', '0xffffffff'], ['0xffffffff', '0xffffffff'], ), + // canCallOnly (not Transfer Value) PERMISSIONS.CALL, + combineAllowedCalls( + [CALLTYPE.CALL, CALLTYPE.CALL], + [recipient.address, recipientUP.address], + ['0xffffffff', '0xffffffff'], + ['0xffffffff', '0xffffffff'], + ), + // canNeitherCallNorTransferValue + PERMISSIONS.SIGN, + combineAllowedCalls( + // we set call types but test that it should not reach the point of checking the call types + [ + combineCallTypes(CALLTYPE.VALUE, CALLTYPE.CALL), + combineCallTypes(CALLTYPE.VALUE, CALLTYPE.CALL), + ], + [recipient.address, recipientUP.address], + ['0xffffffff', '0xffffffff'], + ['0xffffffff', '0xffffffff'], + ), ]; await setupKeyManager(context, permissionsKeys, permissionsValues); @@ -135,7 +161,7 @@ export const shouldBehaveLikePermissionTransferValue = ( describe('when transferring value without bytes `_data`', () => { const data = '0x'; - it('should pass when caller has ALL PERMISSIONS', async () => { + it('should pass when controller has ALL PERMISSIONS', async () => { const amount = ethers.utils.parseEther('3'); const transferPayload = universalProfileInterface.encodeFunctionData('execute', [ @@ -157,7 +183,7 @@ export const shouldBehaveLikePermissionTransferValue = ( ); }); - it('should pass when caller has permission TRANSFERVALUE only', async () => { + it('should pass when controller has permission TRANSFERVALUE only', async () => { const amount = ethers.utils.parseEther('3'); const transferPayload = universalProfileInterface.encodeFunctionData('execute', [ @@ -175,7 +201,7 @@ export const shouldBehaveLikePermissionTransferValue = ( ); }); - it('should pass when caller has permission TRANSFERVALUE + CALL', async () => { + it('should pass when controller has permission TRANSFERVALUE + CALL', async () => { const amount = ethers.utils.parseEther('3'); const transferPayload = universalProfileInterface.encodeFunctionData('execute', [ @@ -193,7 +219,7 @@ export const shouldBehaveLikePermissionTransferValue = ( ); }); - it('should fail when caller does not have permission TRANSFERVALUE', async () => { + it('should fail when controller has permission CALL only but not TRANSFERVALUE', async () => { const initialBalanceUP = await provider.getBalance(context.universalProfile.address); const initialBalanceRecipient = await provider.getBalance(recipient.address); @@ -204,9 +230,34 @@ export const shouldBehaveLikePermissionTransferValue = ( data, ]); - await expect(context.keyManager.connect(cannotTransferValue).execute(transferPayload)) + await expect(context.keyManager.connect(canCallOnly).execute(transferPayload)) .to.be.revertedWithCustomError(context.keyManager, 'NotAuthorised') - .withArgs(cannotTransferValue.address, 'TRANSFERVALUE'); + .withArgs(canCallOnly.address, 'TRANSFERVALUE'); + + const newBalanceUP = await provider.getBalance(context.universalProfile.address); + const newBalanceRecipient = await provider.getBalance(recipient.address); + + // verify that native token balances have not changed + expect(newBalanceUP).to.equal(initialBalanceUP); + expect(initialBalanceRecipient).to.equal(newBalanceRecipient); + }); + + it('should fail when controller has neither CALL nor TRANSFERVALUE permissions', async () => { + const initialBalanceUP = await provider.getBalance(context.universalProfile.address); + const initialBalanceRecipient = await provider.getBalance(recipient.address); + + const transferPayload = universalProfileInterface.encodeFunctionData('execute', [ + OPERATION_TYPES.CALL, + recipient.address, + ethers.utils.parseEther('3'), + data, + ]); + + await expect( + context.keyManager.connect(canNeitherCallNorTransferValue).execute(transferPayload), + ) + .to.be.revertedWithCustomError(context.keyManager, 'NotAuthorised') + .withArgs(canNeitherCallNorTransferValue.address, 'TRANSFERVALUE'); const newBalanceUP = await provider.getBalance(context.universalProfile.address); const newBalanceRecipient = await provider.getBalance(recipient.address); @@ -220,7 +271,7 @@ export const shouldBehaveLikePermissionTransferValue = ( describe('when transferring value with bytes `_data`', () => { const data = '0xaabbccdd'; - it('should pass when caller has ALL PERMISSIONS', async () => { + it('should pass when controller has ALL PERMISSIONS', async () => { const initialBalanceUP = await provider.getBalance(context.universalProfile.address); const initialBalanceRecipient = await provider.getBalance(recipient.address); @@ -241,7 +292,7 @@ export const shouldBehaveLikePermissionTransferValue = ( expect(newBalanceRecipient).to.be.gt(initialBalanceRecipient); }); - it('should pass when caller has permission TRANSFERVALUE + CALL', async () => { + it('should pass when controller has permission TRANSFERVALUE + CALL', async () => { const amount = ethers.utils.parseEther('3'); const transferPayload = universalProfileInterface.encodeFunctionData('execute', [ @@ -259,7 +310,7 @@ export const shouldBehaveLikePermissionTransferValue = ( ); }); - it('should fail when caller has permission TRANSFERVALUE only', async () => { + it('should fail when controller has permission TRANSFERVALUE only', async () => { const initialBalanceUP = await provider.getBalance(context.universalProfile.address); const initialBalanceRecipient = await provider.getBalance(recipient.address); @@ -282,7 +333,7 @@ export const shouldBehaveLikePermissionTransferValue = ( expect(initialBalanceRecipient).to.equal(newBalanceRecipient); }); - it('should fail when caller does not have permission TRANSFERVALUE', async () => { + it('should fail when controller has permission CALL only but not TRANSFERVALUE', async () => { const initialBalanceUP = await provider.getBalance(context.universalProfile.address); const initialBalanceRecipient = await provider.getBalance(recipient.address); @@ -293,9 +344,34 @@ export const shouldBehaveLikePermissionTransferValue = ( data, ]); - await expect(context.keyManager.connect(cannotTransferValue).execute(transferPayload)) + await expect(context.keyManager.connect(canCallOnly).execute(transferPayload)) .to.be.revertedWithCustomError(context.keyManager, 'NotAuthorised') - .withArgs(cannotTransferValue.address, 'TRANSFERVALUE'); + .withArgs(canCallOnly.address, 'TRANSFERVALUE'); + + const newBalanceUP = await provider.getBalance(context.universalProfile.address); + const newBalanceRecipient = await provider.getBalance(recipient.address); + + // verify that native token balances have not changed + expect(newBalanceUP).to.equal(initialBalanceUP); + expect(initialBalanceRecipient).to.equal(newBalanceRecipient); + }); + + it('should fail when controller has neither CALL nor TRANSFERVALUE permissions', async () => { + const initialBalanceUP = await provider.getBalance(context.universalProfile.address); + const initialBalanceRecipient = await provider.getBalance(recipient.address); + + const transferPayload = universalProfileInterface.encodeFunctionData('execute', [ + OPERATION_TYPES.CALL, + recipient.address, + ethers.utils.parseEther('3'), + data, + ]); + + await expect( + context.keyManager.connect(canNeitherCallNorTransferValue).execute(transferPayload), + ) + .to.be.revertedWithCustomError(context.keyManager, 'NotAuthorised') + .withArgs(canNeitherCallNorTransferValue.address, 'TRANSFERVALUE'); const newBalanceUP = await provider.getBalance(context.universalProfile.address); const newBalanceRecipient = await provider.getBalance(recipient.address); @@ -309,7 +385,7 @@ export const shouldBehaveLikePermissionTransferValue = ( describe('when transferring value with graffiti `_data` (prefixed with `bytes4(0)`)', () => { const data = '0x00000000aabbccdd'; - it('should fail when caller has permission TRANSFERVALUE only', async () => { + it('should fail when controller has permission TRANSFERVALUE only', async () => { const initialBalanceUP = await provider.getBalance(context.universalProfile.address); const initialBalanceRecipient = await provider.getBalance(recipient.address); @@ -334,6 +410,56 @@ export const shouldBehaveLikePermissionTransferValue = ( expect(initialBalanceRecipient).to.equal(newBalanceRecipient); }); + it('it should fail when controller has permission CALL only', async () => { + const initialBalanceUP = await provider.getBalance(context.universalProfile.address); + const initialBalanceRecipient = await provider.getBalance(recipient.address); + + const transferPayload = universalProfileInterface.encodeFunctionData('execute', [ + OPERATION_TYPES.CALL, + recipient.address, + ethers.utils.parseEther('3'), + data, + ]); + + await expect(context.keyManager.connect(canCallOnly)['execute(bytes)'](transferPayload)) + .to.be.revertedWithCustomError(context.keyManager, 'NotAuthorised') + .withArgs(canCallOnly.address, 'TRANSFERVALUE'); + + const newBalanceUP = await provider.getBalance(context.universalProfile.address); + const newBalanceRecipient = await provider.getBalance(recipient.address); + + // verify that native token balances have not changed + expect(newBalanceUP).to.equal(initialBalanceUP); + expect(initialBalanceRecipient).to.equal(newBalanceRecipient); + }); + + it('it should fail when caller has neither permissions CALL nor TRANSFERVALUE', async () => { + const initialBalanceUP = await provider.getBalance(context.universalProfile.address); + const initialBalanceRecipient = await provider.getBalance(recipient.address); + + const transferPayload = universalProfileInterface.encodeFunctionData('execute', [ + OPERATION_TYPES.CALL, + recipient.address, + ethers.utils.parseEther('3'), + data, + ]); + + await expect( + context.keyManager + .connect(canNeitherCallNorTransferValue) + ['execute(bytes)'](transferPayload), + ) + .to.be.revertedWithCustomError(context.keyManager, 'NotAuthorised') + .withArgs(canNeitherCallNorTransferValue.address, 'TRANSFERVALUE'); + + const newBalanceUP = await provider.getBalance(context.universalProfile.address); + const newBalanceRecipient = await provider.getBalance(recipient.address); + + // verify that native token balances have not changed + expect(newBalanceUP).to.equal(initialBalanceUP); + expect(initialBalanceRecipient).to.equal(newBalanceRecipient); + }); + it('should pass when caller has permission TRANSFERVALUE + CALL', async () => { const amount = ethers.utils.parseEther('3'); @@ -451,7 +577,7 @@ export const shouldBehaveLikePermissionTransferValue = ( describe('when transferring value with graffiti `_data` (prefixed with `bytes4(0)`)', () => { const data = '0x00000000aabbccdd'; - it('should fail when caller has permission TRANSFERVALUE only', async () => { + it('should fail when controller has permission TRANSFERVALUE only', async () => { const initialBalanceUP = await provider.getBalance(context.universalProfile.address); const initialBalanceRecipient = await provider.getBalance(recipientUP.address); @@ -476,7 +602,30 @@ export const shouldBehaveLikePermissionTransferValue = ( expect(initialBalanceRecipient).to.equal(newBalanceRecipient); }); - it('should pass when caller has permission TRANSFERVALUE + CALL', async () => { + it('should fail when controller has permission CALL only', async () => { + const initialBalanceUP = await provider.getBalance(context.universalProfile.address); + const initialBalanceRecipient = await provider.getBalance(recipientUP.address); + + const transferPayload = universalProfileInterface.encodeFunctionData('execute', [ + OPERATION_TYPES.CALL, + recipientUP.address, + ethers.utils.parseEther('3'), + data, + ]); + + await expect(context.keyManager.connect(canCallOnly)['execute(bytes)'](transferPayload)) + .to.be.revertedWithCustomError(context.keyManager, 'NotAuthorised') + .withArgs(canCallOnly.address, 'TRANSFERVALUE'); + + const newBalanceUP = await provider.getBalance(context.universalProfile.address); + const newBalanceRecipient = await provider.getBalance(recipientUP.address); + + // verify that native token balances have not changed + expect(newBalanceUP).to.equal(initialBalanceUP); + expect(initialBalanceRecipient).to.equal(newBalanceRecipient); + }); + + it('should pass when controller has permission TRANSFERVALUE + CALL', async () => { const amount = ethers.utils.parseEther('3'); const transferPayload = universalProfileInterface.encodeFunctionData('execute', [ diff --git a/tests/foundry/LSP6KeyManager/LSP6AllowedCallsTest.t.sol b/tests/foundry/LSP6KeyManager/LSP6AllowedCallsTest.t.sol new file mode 100644 index 000000000..7b75c7c43 --- /dev/null +++ b/tests/foundry/LSP6KeyManager/LSP6AllowedCallsTest.t.sol @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.4; + +// libraries +import "forge-std/Test.sol"; +import { + LSP2Utils +} from "../../../contracts/LSP2ERC725YJSONSchema/LSP2Utils.sol"; + +// modules +import {UniversalProfile} from "../../../contracts/UniversalProfile.sol"; +import { + KeyManagerInternalTester +} from "../../../contracts/Mocks/KeyManager/KeyManagerInternalsTester.sol"; + +// constants +import { + OPERATION_0_CALL, + OPERATION_3_STATICCALL, + OPERATION_4_DELEGATECALL +} from "@erc725/smart-contracts/contracts/constants.sol"; +import { + _LSP6KEY_ADDRESSPERMISSIONS_ALLOWEDCALLS_PREFIX, + _ALLOWEDCALLS_TRANSFERVALUE, + _ALLOWEDCALLS_CALL, + _ALLOWEDCALLS_STATICCALL, + _ALLOWEDCALLS_DELEGATECALL +} from "../../../contracts/LSP6KeyManager/LSP6Constants.sol"; + +// errors to test +import {NotAllowedCall} from "../../../contracts/LSP6KeyManager/LSP6Errors.sol"; + +// mock contracts for testing +import { + FallbackInitializer +} from "../../../contracts/Mocks/FallbackInitializer.sol"; +import {TargetContract} from "../../../contracts/Mocks/TargetContract.sol"; + +contract LSP6AllowedCallsTest is Test { + using LSP2Utils for *; + + UniversalProfile universalProfile; + KeyManagerInternalTester keyManager; + + TargetContract targetContract; + FallbackInitializer targetWithFallback; + + function setUp() public { + universalProfile = new UniversalProfile(address(this)); + keyManager = new KeyManagerInternalTester(address(universalProfile)); + + targetContract = new TargetContract(); + targetWithFallback = new FallbackInitializer(); + } + + function _setupCallTypes( + bytes4 callTypesAllowed, + address contractToAllow, + bytes4 allowedSelector + ) internal { + // setup allowed calls for this controller, when we will read them from storage + bytes32 allowedCallsDataKey = LSP2Utils.generateMappingWithGroupingKey({ + keyPrefix: _LSP6KEY_ADDRESSPERMISSIONS_ALLOWEDCALLS_PREFIX, + bytes20Value: bytes20(address(this)) + }); + + bytes memory allowedCallsDataValue = abi.encodePacked( + hex"0020", // 2 bytes to specify the entry is 32 bytes long (32 = 0x0020 in hex) + callTypesAllowed, // restrictionOperations (= callTypes allowed) + contractToAllow, // address + bytes4(0xffffffff), // any standard + allowedSelector // function + ); + + universalProfile.setData(allowedCallsDataKey, allowedCallsDataValue); + } + + function testShouldRevertWithEmptyMessageCallWithCallTypeAllowedValueOnly() + public + { + // setup allowed calls for this controller, when we will read them from storage + _setupCallTypes( + _ALLOWEDCALLS_TRANSFERVALUE, + address(targetContract), + bytes4(0xffffffff) // for any function + ); + + bytes memory expectedRevertData = abi.encodeWithSelector( + NotAllowedCall.selector, + address(this), + targetContract, + bytes4(0) + ); + + // Test with CALL + vm.expectRevert(expectedRevertData); + keyManager.verifyAllowedCall( + address(this), + OPERATION_0_CALL, + address(targetContract), + 0, + "" + ); + + // Test with STATICCALL + vm.expectRevert(expectedRevertData); + keyManager.verifyAllowedCall( + address(this), + OPERATION_3_STATICCALL, + address(targetContract), + 0, + "" + ); + + // Test with DELEGATECALL + vm.expectRevert(expectedRevertData); + keyManager.verifyAllowedCall( + address(this), + OPERATION_4_DELEGATECALL, + address(targetContract), + 0, + "" + ); + } + + function testFail_ShouldRevertForAnyMessageCallToTargetWithNoCallTypeAllowed( + uint8 operationType, + uint256 value, + bytes memory callData + ) public { + _setupCallTypes(bytes4(0), address(targetContract), bytes4(0xffffffff)); // for any function + + // We don't test for operation `CREATE` or `CREATE2` + vm.assume(operationType != 1 && operationType != 2); + // we should use a valid operation type + vm.assume(operationType <= 4); + + keyManager.verifyAllowedCall( + address(this), + uint256(operationType), + address(targetContract), + value, + callData + ); + } + + function testFail_ShouldRevertWithEmptyCallNoValueWhenAssociatedCallTypeIsNotSet( + uint8 operationType, + bytes4 callTypeToGrant + ) public { + // We don't test for operation `CREATE` or `CREATE2` + vm.assume(operationType != 1 && operationType != 2); + // we should use a valid operation type + vm.assume(operationType <= 4); + + // Check for testing that the callType is not set for the associated operationType + if (operationType == OPERATION_0_CALL) { + vm.assume( + callTypeToGrant & _ALLOWEDCALLS_CALL != _ALLOWEDCALLS_CALL + ); + } + + if (operationType == OPERATION_3_STATICCALL) { + vm.assume( + callTypeToGrant & _ALLOWEDCALLS_STATICCALL != + _ALLOWEDCALLS_STATICCALL + ); + } + + if (operationType == OPERATION_4_DELEGATECALL) { + vm.assume( + callTypeToGrant & _ALLOWEDCALLS_DELEGATECALL != + _ALLOWEDCALLS_DELEGATECALL + ); + } + + _setupCallTypes( + callTypeToGrant, + address(targetWithFallback), + bytes4(0xffffffff) + ); // for any function + + keyManager.verifyAllowedCall( + address(this), + uint256(operationType), + address(targetWithFallback), + 0, + "" + ); + } + + function test_ShouldPassWithEmptyCallNoValueWhenAssociatedCallTypeIsSet( + uint8 operationType, + bytes4 callTypeToGrant + ) public { + // We don't test for operation `CREATE` or `CREATE2` + vm.assume(operationType != 1 && operationType != 2); + // we should use a valid operation type + vm.assume(operationType <= 4); + + // We should have at least one bit set in the callTypes + vm.assume(callTypeToGrant != bytes4(0)); + + // Check for testing that the callType is not set for the associated operationType + if (operationType == OPERATION_0_CALL) { + vm.assume( + callTypeToGrant & _ALLOWEDCALLS_CALL == _ALLOWEDCALLS_CALL + ); + } + + if (operationType == OPERATION_3_STATICCALL) { + vm.assume( + callTypeToGrant & _ALLOWEDCALLS_STATICCALL == + _ALLOWEDCALLS_STATICCALL + ); + } + + if (operationType == OPERATION_4_DELEGATECALL) { + vm.assume( + callTypeToGrant & _ALLOWEDCALLS_DELEGATECALL == + _ALLOWEDCALLS_DELEGATECALL + ); + } + + _setupCallTypes( + callTypeToGrant, + address(targetWithFallback), + bytes4(0xffffffff) + ); // for any function + + keyManager.verifyAllowedCall( + address(this), + uint256(operationType), + address(targetWithFallback), + 0, + "" + ); + } + + function test_ShouldPassWithCallDataAs0x00000000WhenCallTypeAllowBytes4ZeroSelector( + uint8 operationType, + bytes4 callTypeToGrant + ) public { + // We don't test for operation `CREATE` or `CREATE2` + vm.assume(operationType != 1 && operationType != 2); + // we should use a valid operation type + vm.assume(operationType <= 4); + + // Check for testing that the callType is not set for the associated operationType + if (operationType == OPERATION_0_CALL) { + vm.assume( + callTypeToGrant & _ALLOWEDCALLS_CALL == _ALLOWEDCALLS_CALL + ); + } + + if (operationType == OPERATION_3_STATICCALL) { + vm.assume( + callTypeToGrant & _ALLOWEDCALLS_STATICCALL == + _ALLOWEDCALLS_STATICCALL + ); + } + + if (operationType == OPERATION_4_DELEGATECALL) { + vm.assume( + callTypeToGrant & _ALLOWEDCALLS_DELEGATECALL == + _ALLOWEDCALLS_DELEGATECALL + ); + } + + _setupCallTypes( + callTypeToGrant, + address(targetWithFallback), + bytes4(0) // only for the bytes4(0) selector + ); + + keyManager.verifyAllowedCall( + address(this), + uint256(operationType), + address(targetWithFallback), + 0, + hex"00000000" + ); + } + + function testFail_ShouldRevertWithCallDataAs0x00000000WhenCallTypeDoesNotAllowBytes4ZeroSelector( + uint8 operationType, + bytes4 callTypeToGrant, + bytes4 randomFunctionSelectorToAllow + ) public { + // We don't test for operation `CREATE` or `CREATE2` + vm.assume(operationType != 1 && operationType != 2); + // we should use a valid operation type + vm.assume(operationType <= 4); + + // exclude the bytes4(0) selector for graffiti, and 0xffffffff for any function allowed + vm.assume( + randomFunctionSelectorToAllow != bytes4(0) && + randomFunctionSelectorToAllow != 0xffffffff + ); + + // Check for testing that the callType is not set for the associated operationType + if (operationType == OPERATION_0_CALL) { + vm.assume( + callTypeToGrant & _ALLOWEDCALLS_CALL == _ALLOWEDCALLS_CALL + ); + } + + if (operationType == OPERATION_3_STATICCALL) { + vm.assume( + callTypeToGrant & _ALLOWEDCALLS_STATICCALL == + _ALLOWEDCALLS_STATICCALL + ); + } + + if (operationType == OPERATION_4_DELEGATECALL) { + vm.assume( + callTypeToGrant & _ALLOWEDCALLS_DELEGATECALL == + _ALLOWEDCALLS_DELEGATECALL + ); + } + + _setupCallTypes( + callTypeToGrant, + address(targetWithFallback), + randomFunctionSelectorToAllow + ); + + keyManager.verifyAllowedCall( + address(this), + uint256(operationType), + address(targetWithFallback), + 0, + hex"00000000" + ); + } +} From c0090c6a6d7e3473702cd85549a1fa2670b00bff Mon Sep 17 00:00:00 2001 From: Jean Cvllr <31145285+CJ42@users.noreply.github.com> Date: Fri, 3 Nov 2023 12:44:33 +0000 Subject: [PATCH 13/22] chore: bootstrap releases for path: . + add `Version.sol` in file to update version for (#785) * chore: bootstrap releases for path: . * build: add `Version.sol` as extra file to update --- .release-please-manifest.json | 3 +++ contracts/Version.sol | 5 +++++ release-please-config.json | 14 ++++++++++++++ 3 files changed, 22 insertions(+) create mode 100644 .release-please-manifest.json create mode 100644 release-please-config.json diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 000000000..8032c17e8 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.12.0" +} diff --git a/contracts/Version.sol b/contracts/Version.sol index 099e75ebc..f0a27b093 100644 --- a/contracts/Version.sol +++ b/contracts/Version.sol @@ -2,8 +2,13 @@ pragma solidity ^0.8.4; abstract contract Version { + // DO NOT CHANGE + // Comments block below is used by release-please to automatically update the version in this file. + // x-release-please-start-version string internal constant _VERSION = "0.12.0"; + // x-release-please-end + /** * @dev Get the version of the contract. * @notice Contract version. diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 000000000..399b7e0f9 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,14 @@ +{ + "packages": { + ".": { + "changelog-path": "CHANGELOG.md", + "release-type": "node", + "bump-minor-pre-major": true, + "bump-patch-for-minor-pre-major": false, + "draft": false, + "prerelease": false, + "extra-files": ["contracts/Version.sol"] + } + }, + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json" +} From bff93e7878848ac798c60f6c274e64dcb26b2a30 Mon Sep 17 00:00:00 2001 From: Andreas Richter <708186+richtera@users.noreply.github.com> Date: Mon, 6 Nov 2023 03:25:48 -0500 Subject: [PATCH 14/22] fix: Adjust missing verificationData and verificationFunction (#788) --- constants.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/constants.ts b/constants.ts index aca2e5f28..fda71a580 100644 --- a/constants.ts +++ b/constants.ts @@ -123,8 +123,8 @@ export type LSP4DigitalAssetMetadata = { export type ImageMetadata = { width: number; height: number; - hashFunction: string; - hash: string; + verificationFunction: string; + verificationData: string; url: string; }; @@ -134,8 +134,8 @@ export type LinkMetadata = { }; export type AssetMetadata = { - hashFunction: string; - hash: string; + verificationFunction: string; + verificationData: string; url: string; fileType: string; }; From c1574a8b58ef78a64f3edf086c652ac4ad4a2931 Mon Sep 17 00:00:00 2001 From: Jean Cvllr <31145285+CJ42@users.noreply.github.com> Date: Mon, 6 Nov 2023 09:38:24 +0000 Subject: [PATCH 15/22] chore: add notice in Natspec for `isValidSignature` (#787) --- contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol | 5 +++++ contracts/LSP6KeyManager/LSP6KeyManagerCore.sol | 5 +++++ docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md | 6 ++++++ docs/contracts/LSP6KeyManager/LSP6KeyManager.md | 6 ++++++ docs/contracts/UniversalProfile.md | 6 ++++++ 5 files changed, 28 insertions(+) diff --git a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol index 848675f96..463996fd8 100644 --- a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol +++ b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol @@ -725,6 +725,11 @@ abstract contract LSP0ERC725AccountCore is * @param signature A signature that can validate the previous parameter (Hash). * * @return returnedStatus A `bytes4` value that indicates if the signature is valid or not. + * + * @custom:warning This function does not enforce by default the inclusion of the address of this contract in the signature digest. + * It is recommended that protocols or applications using this contract include the targeted address (= this contract) in the data to sign. + * To ensure that a signature is valid for a specific LSP0ERC725Account and prevent signatures from the same EOA to be replayed + * across different LSP0ERC725Accounts. */ function isValidSignature( bytes32 dataHash, diff --git a/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol b/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol index 9edf51cff..de33626c7 100644 --- a/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol +++ b/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol @@ -152,6 +152,11 @@ abstract contract LSP6KeyManagerCore is * If the signer is a controller with the permission `SIGN`, it will return the ERC1271 success value. * * @return returnedStatus `0x1626ba7e` on success, or `0xffffffff` on failure. + * + * @custom:warning This function does not enforce by default the inclusion of the address of this contract in the signature digest. + * It is recommended that protocols or applications using this contract include the targeted address (= this contract) in the data to sign. + * To ensure that a signature is valid for a specific LSP6KeyManager and prevent signatures from the same EOA to be replayed + * across different LSP6KeyManager. */ function isValidSignature( bytes32 dataHash, diff --git a/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md b/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md index 5e89b44a5..b4e4d8be0 100644 --- a/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md +++ b/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md @@ -487,6 +487,12 @@ Get in the ERC725Y storage the bytes data stored at multiple data keys `dataKeys ::: +:::caution Warning + +This function does not enforce by default the inclusion of the address of this contract in the signature digest. It is recommended that protocols or applications using this contract include the targeted address (= this contract) in the data to sign. To ensure that a signature is valid for a specific LSP0ERC725Account and prevent signatures from the same EOA to be replayed across different LSP0ERC725Accounts. + +::: + ```solidity function isValidSignature( bytes32 dataHash, diff --git a/docs/contracts/LSP6KeyManager/LSP6KeyManager.md b/docs/contracts/LSP6KeyManager/LSP6KeyManager.md index e5dd80fec..514841938 100644 --- a/docs/contracts/LSP6KeyManager/LSP6KeyManager.md +++ b/docs/contracts/LSP6KeyManager/LSP6KeyManager.md @@ -302,6 +302,12 @@ Get the nonce for a specific `from` address that can be used for signing relay t ::: +:::caution Warning + +This function does not enforce by default the inclusion of the address of this contract in the signature digest. It is recommended that protocols or applications using this contract include the targeted address (= this contract) in the data to sign. To ensure that a signature is valid for a specific LSP6KeyManager and prevent signatures from the same EOA to be replayed across different LSP6KeyManager. + +::: + ```solidity function isValidSignature( bytes32 dataHash, diff --git a/docs/contracts/UniversalProfile.md b/docs/contracts/UniversalProfile.md index 4ec73c3b3..0531b4ef2 100644 --- a/docs/contracts/UniversalProfile.md +++ b/docs/contracts/UniversalProfile.md @@ -430,6 +430,12 @@ Get in the ERC725Y storage the bytes data stored at multiple data keys `dataKeys ::: +:::caution Warning + +This function does not enforce by default the inclusion of the address of this contract in the signature digest. It is recommended that protocols or applications using this contract include the targeted address (= this contract) in the data to sign. To ensure that a signature is valid for a specific LSP0ERC725Account and prevent signatures from the same EOA to be replayed across different LSP0ERC725Accounts. + +::: + ```solidity function isValidSignature( bytes32 dataHash, From a855dd1999d38d54b6a0f7999c5e2a8538653214 Mon Sep 17 00:00:00 2001 From: Skima Harvey <64636974+skimaharvey@users.noreply.github.com> Date: Mon, 6 Nov 2023 10:59:01 +0000 Subject: [PATCH 16/22] refactor: use gas estimate in deployements scripts (#792) Co-authored-by: Jean Cvllr <31145285+CJ42@users.noreply.github.com> --- deploy/001_deploy_universal_profile.ts | 4 +++- deploy/002_deploy_key_manager.ts | 4 +++- deploy/003_deploy_universal_receiver_delegate.ts | 4 +++- deploy/005_deploy_universal_receiver_delegate_vault.ts | 4 +++- deploy/006_deploy_base_universal_profile.ts | 4 +++- deploy/007_deploy_base_key_manager.ts | 4 +++- deploy/008_deploy_lsp7_mintable.ts | 4 +++- deploy/009_deploy_lsp8_mintable.ts | 4 +++- deploy/010_deploy_base_lsp7_mintable.ts | 4 +++- deploy/011_deploy_base_lsp8_mintable.ts | 4 +++- deploy/012_deploy_vault.ts | 4 +++- deploy/013_deploy_base_vault.ts | 4 +++- 12 files changed, 36 insertions(+), 12 deletions(-) diff --git a/deploy/001_deploy_universal_profile.ts b/deploy/001_deploy_universal_profile.ts index 999e0ac3d..3c35d1c2b 100644 --- a/deploy/001_deploy_universal_profile.ts +++ b/deploy/001_deploy_universal_profile.ts @@ -9,10 +9,12 @@ const deployUniversalProfile: DeployFunction = async ({ const { deploy } = deployments; const { owner } = await getNamedAccounts(); + const gasPrice = await ethers.provider.getGasPrice(); + await deploy('UniversalProfile', { from: owner, args: [owner], - gasPrice: ethers.BigNumber.from(20_000_000_000), // in wei + gasPrice, log: true, }); }; diff --git a/deploy/002_deploy_key_manager.ts b/deploy/002_deploy_key_manager.ts index 9be14acd4..0f357baab 100644 --- a/deploy/002_deploy_key_manager.ts +++ b/deploy/002_deploy_key_manager.ts @@ -11,10 +11,12 @@ const deployKeyManager: DeployFunction = async ({ const UniversalProfile = await deployments.get('UniversalProfile'); + const gasPrice = await ethers.provider.getGasPrice(); + await deploy('LSP6KeyManager', { from: owner, args: [UniversalProfile.address], - gasPrice: ethers.BigNumber.from(20_000_000_000), // in wei + gasPrice, log: true, }); }; diff --git a/deploy/003_deploy_universal_receiver_delegate.ts b/deploy/003_deploy_universal_receiver_delegate.ts index f0017cd09..a3f7d4d4a 100644 --- a/deploy/003_deploy_universal_receiver_delegate.ts +++ b/deploy/003_deploy_universal_receiver_delegate.ts @@ -10,9 +10,11 @@ const deployUniversalReceiverDelegateUPDeterministic: DeployFunction = async ({ const { deploy } = deployments; const { owner: deployer } = await getNamedAccounts(); + const gasPrice = await ethers.provider.getGasPrice(); + await deploy('LSP1UniversalReceiverDelegateUP', { from: deployer, - gasPrice: ethers.BigNumber.from(20_000_000_000), // in wei + gasPrice, log: true, deterministicDeployment: SALT, }); diff --git a/deploy/005_deploy_universal_receiver_delegate_vault.ts b/deploy/005_deploy_universal_receiver_delegate_vault.ts index d82402909..795933b4b 100644 --- a/deploy/005_deploy_universal_receiver_delegate_vault.ts +++ b/deploy/005_deploy_universal_receiver_delegate_vault.ts @@ -10,9 +10,11 @@ const deployUniversalReceiverDelegateVaultDeterministic: DeployFunction = async const { deploy } = deployments; const { owner: deployer } = await getNamedAccounts(); + const gasPrice = await ethers.provider.getGasPrice(); + await deploy('LSP1UniversalReceiverDelegateVault', { from: deployer, - gasPrice: ethers.BigNumber.from(20_000_000_000), // in wei + gasPrice, log: true, deterministicDeployment: SALT, }); diff --git a/deploy/006_deploy_base_universal_profile.ts b/deploy/006_deploy_base_universal_profile.ts index de728569b..1064f4c67 100644 --- a/deploy/006_deploy_base_universal_profile.ts +++ b/deploy/006_deploy_base_universal_profile.ts @@ -10,10 +10,12 @@ const deployBaseUniversalProfileDeterministic: DeployFunction = async ({ const { deploy } = deployments; const { owner: deployer } = await getNamedAccounts(); + const gasPrice = await ethers.provider.getGasPrice(); + await deploy('UniversalProfileInit', { from: deployer, log: true, - gasPrice: ethers.BigNumber.from(20_000_000_000), // in wei + gasPrice, deterministicDeployment: SALT, }); }; diff --git a/deploy/007_deploy_base_key_manager.ts b/deploy/007_deploy_base_key_manager.ts index a382df610..2b2ed835e 100644 --- a/deploy/007_deploy_base_key_manager.ts +++ b/deploy/007_deploy_base_key_manager.ts @@ -10,11 +10,13 @@ const deployBaseKeyManagerDeterministic: DeployFunction = async ({ const { deploy } = deployments; const { owner: deployer } = await getNamedAccounts(); + const gasPrice = await ethers.provider.getGasPrice(); + await deploy('LSP6KeyManagerInit', { from: deployer, log: true, gasLimit: 5_000_000, - gasPrice: ethers.BigNumber.from(20_000_000_000), // in wei + gasPrice, deterministicDeployment: SALT, }); }; diff --git a/deploy/008_deploy_lsp7_mintable.ts b/deploy/008_deploy_lsp7_mintable.ts index 728be5323..0f1ecc62e 100644 --- a/deploy/008_deploy_lsp7_mintable.ts +++ b/deploy/008_deploy_lsp7_mintable.ts @@ -9,10 +9,12 @@ const deployLSP7Mintable: DeployFunction = async ({ const { deploy } = deployments; const { owner } = await getNamedAccounts(); + const gasPrice = await ethers.provider.getGasPrice(); + await deploy('LSP7Mintable', { from: owner, args: ['LSP7 Mintable', 'LSP7M', owner, false], - gasPrice: ethers.BigNumber.from(20_000_000_000), // in wei, + gasPrice, log: true, }); }; diff --git a/deploy/009_deploy_lsp8_mintable.ts b/deploy/009_deploy_lsp8_mintable.ts index e75cc6810..1ba09cee4 100644 --- a/deploy/009_deploy_lsp8_mintable.ts +++ b/deploy/009_deploy_lsp8_mintable.ts @@ -9,10 +9,12 @@ const deployLSP8MintableDeterministic: DeployFunction = async ({ const { deploy } = deployments; const { owner: deployer } = await getNamedAccounts(); + const gasPrice = await ethers.provider.getGasPrice(); + await deploy('LSP8Mintable', { from: deployer, args: ['LSP8 Mintable', 'LSP8M', deployer], - gasPrice: ethers.BigNumber.from(20_000_000_000), // in wei, + gasPrice, log: true, deterministicDeployment: true, }); diff --git a/deploy/010_deploy_base_lsp7_mintable.ts b/deploy/010_deploy_base_lsp7_mintable.ts index 984a80921..e6e5a708a 100644 --- a/deploy/010_deploy_base_lsp7_mintable.ts +++ b/deploy/010_deploy_base_lsp7_mintable.ts @@ -10,9 +10,11 @@ const deployBaseLSP7MintableDeterministic: DeployFunction = async ({ const { deploy } = deployments; const { owner: deployer } = await getNamedAccounts(); + const gasPrice = await ethers.provider.getGasPrice(); + await deploy('LSP7MintableInit', { from: deployer, - gasPrice: ethers.BigNumber.from(20_000_000_000), // in wei, + gasPrice, log: true, deterministicDeployment: SALT, }); diff --git a/deploy/011_deploy_base_lsp8_mintable.ts b/deploy/011_deploy_base_lsp8_mintable.ts index 2054c1da3..6b2f8915e 100644 --- a/deploy/011_deploy_base_lsp8_mintable.ts +++ b/deploy/011_deploy_base_lsp8_mintable.ts @@ -10,9 +10,11 @@ const deployBaseLSP8Mintable: DeployFunction = async ({ const { deploy } = deployments; const { owner } = await getNamedAccounts(); + const gasPrice = await ethers.provider.getGasPrice(); + await deploy('LSP8MintableInit', { from: owner, - gasPrice: ethers.BigNumber.from(20_000_000_000), // in wei, + gasPrice, log: true, deterministicDeployment: SALT, }); diff --git a/deploy/012_deploy_vault.ts b/deploy/012_deploy_vault.ts index b3db5d6bd..55ab6e4d0 100644 --- a/deploy/012_deploy_vault.ts +++ b/deploy/012_deploy_vault.ts @@ -9,10 +9,12 @@ const deployVault: DeployFunction = async ({ const { deploy } = deployments; const { owner } = await getNamedAccounts(); + const gasPrice = await ethers.provider.getGasPrice(); + await deploy('LSP9Vault', { from: owner, args: [owner], - gasPrice: ethers.BigNumber.from(20_000_000_000), // in wei + gasPrice, log: true, }); }; diff --git a/deploy/013_deploy_base_vault.ts b/deploy/013_deploy_base_vault.ts index c9aaa47d0..b98e67c12 100644 --- a/deploy/013_deploy_base_vault.ts +++ b/deploy/013_deploy_base_vault.ts @@ -10,10 +10,12 @@ const deployBaseVaultDeterministic: DeployFunction = async ({ const { deploy } = deployments; const { owner: deployer } = await getNamedAccounts(); + const gasPrice = await ethers.provider.getGasPrice(); + await deploy('LSP9VaultInit', { from: deployer, log: true, - gasPrice: ethers.BigNumber.from(20_000_000_000), // in wei + gasPrice, deterministicDeployment: SALT, }); }; From e7ef125a1b77d5b3feb85a2b4133dec314ff917a Mon Sep 17 00:00:00 2001 From: Jean Cvllr <31145285+CJ42@users.noreply.github.com> Date: Mon, 6 Nov 2023 13:36:45 +0000 Subject: [PATCH 17/22] chore: adjust Natspec comments + use mixedCase name for `modifier` (#786) * docs: adjust missing Natspec comments * chore: write `modifer` in mixedCase according to Solidity style guide * docs: imporve grammar in Natspec docs for `_nonReentrantBefore` --- contracts/LSP0ERC725Account/LSP0ERC725Account.sol | 6 +++++- .../LSP0ERC725Account/LSP0ERC725AccountCore.sol | 2 +- .../LSP0ERC725AccountInitAbstract.sol | 4 +++- contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol | 4 ++-- .../LSP1UniversalReceiverDelegateUP.sol | 10 +++++----- .../LSP1UniversalReceiverDelegateVault.sol | 6 +++--- contracts/LSP6KeyManager/LSP6KeyManagerCore.sol | 12 +++++++----- contracts/LSP9Vault/LSP9VaultCore.sol | 4 ++-- contracts/Mocks/Executor.sol | 6 ++---- contracts/Mocks/ExecutorLSP20.sol | 6 ++---- contracts/UniversalProfile.sol | 4 +++- contracts/UniversalProfileInit.sol | 10 ++++++---- contracts/UniversalProfileInitAbstract.sol | 2 ++ .../LSP0ERC725Account/LSP0ERC725Account.md | 6 +++++- docs/contracts/LSP6KeyManager/LSP6KeyManager.md | 13 ++++++++----- docs/contracts/LSP9Vault/LSP9Vault.md | 2 +- docs/contracts/UniversalProfile.md | 6 +++++- 17 files changed, 62 insertions(+), 41 deletions(-) diff --git a/contracts/LSP0ERC725Account/LSP0ERC725Account.sol b/contracts/LSP0ERC725Account/LSP0ERC725Account.sol index c8a80460a..fbfaca060 100644 --- a/contracts/LSP0ERC725Account/LSP0ERC725Account.sol +++ b/contracts/LSP0ERC725Account/LSP0ERC725Account.sol @@ -29,7 +29,11 @@ import {_TYPEID_LSP0_VALUE_RECEIVED} from "./LSP0Constants.sol"; contract LSP0ERC725Account is LSP0ERC725AccountCore { /** * @notice Deploying a LSP0ERC725Account contract with owner set to address `initialOwner`. - * @dev Set `initialOwner` as the contract owner. The `constructor` also allows funding the contract on deployment. + * + * @dev Set `initialOwner` as the contract owner. + * - The `constructor` also allows funding the contract on deployment. + * - The `initialOwner` will then be allowed to call protected functions marked with the `onlyOwner` modifier. + * * @param initialOwner The owner of the contract. * * @custom:events diff --git a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol index 463996fd8..2b436afbd 100644 --- a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol +++ b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol @@ -585,7 +585,7 @@ abstract contract LSP0ERC725AccountCore is * - When notifying the previous owner via LSP1, the typeId used must be the `keccak256(...)` hash of [LSP0OwnershipTransferred_SenderNotification]. * - When notifying the new owner via LSP1, the typeId used must be the `keccak256(...)` hash of [LSP0OwnershipTransferred_RecipientNotification]. */ - function acceptOwnership() public virtual override NotInTransferOwnership { + function acceptOwnership() public virtual override notInTransferOwnership { address previousOwner = owner(); address pendingOwnerAddress = pendingOwner(); diff --git a/contracts/LSP0ERC725Account/LSP0ERC725AccountInitAbstract.sol b/contracts/LSP0ERC725Account/LSP0ERC725AccountInitAbstract.sol index 5091710ba..1ba4f4d38 100644 --- a/contracts/LSP0ERC725Account/LSP0ERC725AccountInitAbstract.sol +++ b/contracts/LSP0ERC725Account/LSP0ERC725AccountInitAbstract.sol @@ -24,10 +24,12 @@ abstract contract LSP0ERC725AccountInitAbstract is { /** * @dev Set `initialOwner` as the contract owner. + * The `initialOwner` will then be allowed to call protected functions marked with the `onlyOwner` modifier. * * @param initialOwner The owner of the contract. * - * @custom:warning ERC725X & ERC725Y parent contracts are not initialised as they don't have non-zero initial state. If you decide to add non-zero initial state to any of those contracts, you must initialize them here. + * @custom:warning ERC725X & ERC725Y parent contracts are not initialixed as they don't have non-zero initial state. + * If you decide to add non-zero initial state to any of those contracts, you MUST initialize them here. * * @custom:events * - {UniversalReceiver} event when funding the contract on deployment. diff --git a/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol b/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol index 84d7bd3e0..a5a593803 100644 --- a/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol +++ b/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol @@ -65,7 +65,7 @@ abstract contract LSP14Ownable2Step is ILSP14Ownable2Step, OwnableUnset { /** * @dev reverts when {_inTransferOwnership} variable is true */ - modifier NotInTransferOwnership() virtual { + modifier notInTransferOwnership() virtual { if (_inTransferOwnership) { revert LSP14MustAcceptOwnershipInSeparateTransaction(); } @@ -111,7 +111,7 @@ abstract contract LSP14Ownable2Step is ILSP14Ownable2Step, OwnableUnset { * * @custom:requirements This function can only be called by the {pendingOwner()}. */ - function acceptOwnership() public virtual override NotInTransferOwnership { + function acceptOwnership() public virtual override notInTransferOwnership { address previousOwner = owner(); _acceptOwnership(); diff --git a/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol b/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol index 04eba69bc..6e8c236dd 100644 --- a/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol +++ b/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol @@ -72,7 +72,7 @@ contract LSP1UniversalReceiverDelegateUP is * * @param notifier The address that notified. */ - modifier NotEOA(address notifier) { + modifier notEOA(address notifier) { // solhint-disable-next-line avoid-tx-origin if (notifier == tx.origin) { revert CannotRegisterEOAsAsAssets(notifier); @@ -145,7 +145,7 @@ contract LSP1UniversalReceiverDelegateUP is */ function _tokenSender( address notifier - ) internal NotEOA(notifier) returns (bytes memory) { + ) internal notEOA(notifier) returns (bytes memory) { // if the amount sent is not the full balance, then do not update the keys try ILSP7DigitalAsset(notifier).balanceOf(msg.sender) returns ( uint256 balance @@ -183,7 +183,7 @@ contract LSP1UniversalReceiverDelegateUP is function _tokenRecipient( address notifier, bytes4 interfaceId - ) internal NotEOA(notifier) returns (bytes memory) { + ) internal notEOA(notifier) returns (bytes memory) { // CHECK balance only when the Token contract is already deployed, // not when tokens are being transferred on deployment through the `constructor` if (notifier.code.length != 0) { @@ -223,7 +223,7 @@ contract LSP1UniversalReceiverDelegateUP is */ function _vaultSender( address notifier - ) internal NotEOA(notifier) returns (bytes memory) { + ) internal notEOA(notifier) returns (bytes memory) { (bytes32[] memory dataKeys, bytes[] memory dataValues) = LSP10Utils .generateSentVaultKeys(msg.sender, notifier); @@ -248,7 +248,7 @@ contract LSP1UniversalReceiverDelegateUP is */ function _vaultRecipient( address notifier - ) internal NotEOA(notifier) returns (bytes memory) { + ) internal notEOA(notifier) returns (bytes memory) { (bytes32[] memory dataKeys, bytes[] memory dataValues) = LSP10Utils .generateReceivedVaultKeys(msg.sender, notifier); diff --git a/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol b/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol index d015df45f..097d56453 100644 --- a/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol +++ b/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol @@ -59,7 +59,7 @@ contract LSP1UniversalReceiverDelegateVault is * * @param notifier The address that notified. */ - modifier NotEOA(address notifier) { + modifier notEOA(address notifier) { // solhint-disable-next-line avoid-tx-origin if (notifier == tx.origin) { revert CannotRegisterEOAsAsAssets(notifier); @@ -120,7 +120,7 @@ contract LSP1UniversalReceiverDelegateVault is */ function _tokenSender( address notifier - ) internal NotEOA(notifier) returns (bytes memory) { + ) internal notEOA(notifier) returns (bytes memory) { // if the amount sent is not the full balance, then do not update the keys try ILSP7DigitalAsset(notifier).balanceOf(msg.sender) returns ( uint256 balance @@ -158,7 +158,7 @@ contract LSP1UniversalReceiverDelegateVault is function _tokenRecipient( address notifier, bytes4 interfaceId - ) internal NotEOA(notifier) returns (bytes memory) { + ) internal notEOA(notifier) returns (bytes memory) { // CHECK balance only when the Token contract is already deployed, // not when tokens are being transferred on deployment through the `constructor` if (notifier.code.length != 0) { diff --git a/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol b/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol index de33626c7..96a129091 100644 --- a/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol +++ b/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol @@ -641,9 +641,11 @@ abstract contract LSP6KeyManagerCore is } /** - * @dev Update the status from `_NON_ENTERED` to `_ENTERED` and checks if - * the status is `_ENTERED` in order to revert the call unless the caller has the REENTRANCY permission - * Used in the beginning of the `nonReentrant` modifier, before the method execution starts. + * @dev Check if we are in the context of a reentrant call, by checking if the reentrancy status is `true`. + * - If the status is `true`, the caller (or signer for relay call) MUST have the `REENTRANCY` permission. Otherwise, the call is reverted. + * - If the status is `false`, it is set to `true` only if we are not dealing with a call to the functions `setData` or `setDataBatch`. + * Used at the beginning of the {`lsp20VerifyCall`}, {`_execute`} and {`_executeRelayCall`} functions, before the methods execution starts. + * */ function _nonReentrantBefore( address targetContract, @@ -667,8 +669,8 @@ abstract contract LSP6KeyManagerCore is } /** - * @dev Resets the status to `false` - * Used in the end of the `nonReentrant` modifier after the method execution is terminated + * @dev Resets the reentrancy status to `false` + * Used at the end of the {`lsp20VerifyCall`}, {`_execute`} and {`_executeRelayCall`} functions after the functions' execution is terminated. */ function _nonReentrantAfter(address targetContract) internal virtual { // By storing the original value once again, a refund is triggered (see diff --git a/contracts/LSP9Vault/LSP9VaultCore.sol b/contracts/LSP9Vault/LSP9VaultCore.sol index 650fbc6a6..0f1281117 100644 --- a/contracts/LSP9Vault/LSP9VaultCore.sol +++ b/contracts/LSP9Vault/LSP9VaultCore.sol @@ -455,7 +455,7 @@ contract LSP9VaultCore is * - When notifying the previous owner via LSP1, the typeId used must be the `keccak256(...)` hash of [LSP0OwnershipTransferred_SenderNotification]. * - When notifying the new owner via LSP1, the typeId used must be the `keccak256(...)` hash of [LSP0OwnershipTransferred_RecipientNotification]. */ - function acceptOwnership() public virtual override NotInTransferOwnership { + function acceptOwnership() public virtual override notInTransferOwnership { address previousOwner = owner(); _acceptOwnership(); @@ -625,7 +625,7 @@ contract LSP9VaultCore is } /** - * @dev Modifier restricting the call to the owner of the contract and the UniversalReceiverDelegate + * @dev Internal method restricting the call to the owner of the contract and the UniversalReceiverDelegate */ function _validateAndIdentifyCaller() internal diff --git a/contracts/Mocks/Executor.sol b/contracts/Mocks/Executor.sol index 2a234ad65..4f8704b6b 100644 --- a/contracts/Mocks/Executor.sol +++ b/contracts/Mocks/Executor.sol @@ -26,10 +26,8 @@ contract Executor { LSP6KeyManager private _keyManager; UniversalProfile private _universalProfile; - // payable modifier is required as _account is non-payable by default - // but UniversalProfile has a payable fallback function - constructor(address payable account_, address keyManager_) { - _universalProfile = UniversalProfile(account_); + constructor(UniversalProfile account_, address keyManager_) { + _universalProfile = account_; _keyManager = LSP6KeyManager(keyManager_); } diff --git a/contracts/Mocks/ExecutorLSP20.sol b/contracts/Mocks/ExecutorLSP20.sol index ac77e27c1..ac7a57617 100644 --- a/contracts/Mocks/ExecutorLSP20.sol +++ b/contracts/Mocks/ExecutorLSP20.sol @@ -23,10 +23,8 @@ contract ExecutorLSP20 { UniversalProfile private _universalProfile; - // payable modifier is required as _account is non-payable by default - // but UniversalProfile has a payable fallback function - constructor(address payable account_) { - _universalProfile = UniversalProfile(account_); + constructor(UniversalProfile account_) { + _universalProfile = account_; } // contract calls diff --git a/contracts/UniversalProfile.sol b/contracts/UniversalProfile.sol index cda387553..fddcc5434 100644 --- a/contracts/UniversalProfile.sol +++ b/contracts/UniversalProfile.sol @@ -19,7 +19,9 @@ contract UniversalProfile is LSP0ERC725Account { /** * @notice Deploying a UniversalProfile contract with owner set to address `initialOwner`. * - * @dev Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3UniversalProfile` data key in the ERC725Y data key/value store. The `constructor` also allows funding the contract on deployment. + * @dev Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3UniversalProfile` data key in the ERC725Y data key/value store. + * - The `constructor` is payable and allows funding the contract on deployment. + * - The `initialOwner` will then be allowed to call protected functions marked with the `onlyOwner` modifier. * * @param initialOwner the owner of the contract * diff --git a/contracts/UniversalProfileInit.sol b/contracts/UniversalProfileInit.sol index 06fc853af..38c9bbf1d 100644 --- a/contracts/UniversalProfileInit.sol +++ b/contracts/UniversalProfileInit.sol @@ -12,9 +12,8 @@ import {UniversalProfileInitAbstract} from "./UniversalProfileInitAbstract.sol"; contract UniversalProfileInit is UniversalProfileInitAbstract { /** * @notice deploying a `UniversalProfileInit` base contract to be used behind proxy - * @dev Locks the base contract on deployment, so that it cannot be initialized, owned and controlled by anyone - * after it has been deployed. This is intended so that the sole purpose of this contract is to be used as a base - * contract behind a proxy. + * @dev Locks the base contract on deployment, so that it cannot be initialized, owned and controlled by anyone after it has been deployed. + * This is intended so that the sole purpose of this contract is to be used as a base contract behind a proxy. */ constructor() { _disableInitializers(); @@ -23,13 +22,16 @@ contract UniversalProfileInit is UniversalProfileInitAbstract { /** * @notice Initializing a UniversalProfile contract with owner set to address `initialOwner`. * - * @dev Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3UniversalProfile` data key in the ERC725Y data key/value store. The `constructor` also allows funding the contract on deployment. The `initialOwner` will then be allowed to call protected functions marked with the `onlyOwner` modifier. The `initialize(address)` function also allows funding the contract on initialization. + * @dev Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3UniversalProfile` data key in the ERC725Y data key/value store. + * - The `initialize(address)` function is payable and allows funding the contract on initialization. + * - The `initialOwner` will then be allowed to call protected functions marked with the `onlyOwner` modifier. * * @param initialOwner the owner of the contract * * @custom:events * - {UniversalReceiver} event when funding the contract on deployment. * - {OwnershipTransferred} event when `initialOwner` is set as the contract {owner}. + * - {DataChanged} event when setting the {_LSP3_SUPPORTED_STANDARDS_KEY}. */ function initialize( address initialOwner diff --git a/contracts/UniversalProfileInitAbstract.sol b/contracts/UniversalProfileInitAbstract.sol index 424997c23..b6f39003c 100644 --- a/contracts/UniversalProfileInitAbstract.sol +++ b/contracts/UniversalProfileInitAbstract.sol @@ -22,6 +22,7 @@ abstract contract UniversalProfileInitAbstract is { /** * @dev Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3UniversalProfile` data key in the ERC725Y data key/value store. + * The `initialOwner` will then be allowed to call protected functions marked with the `onlyOwner` modifier. * * @param initialOwner The owner of the contract. * @@ -30,6 +31,7 @@ abstract contract UniversalProfileInitAbstract is * @custom:events * - {UniversalReceiver} event when funding the contract on deployment. * - {OwnershipTransferred} event when `initialOwner` is set as the contract {owner}. + * - {DataChanged} event when setting the {_LSP3_SUPPORTED_STANDARDS_KEY}. */ function _initialize( address initialOwner diff --git a/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md b/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md index b4e4d8be0..adaa4c9e8 100644 --- a/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md +++ b/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md @@ -54,7 +54,11 @@ constructor(address initialOwner); _Deploying a LSP0ERC725Account contract with owner set to address `initialOwner`._ -Set `initialOwner` as the contract owner. The `constructor` also allows funding the contract on deployment. +Set `initialOwner` as the contract owner. + +- The `constructor` also allows funding the contract on deployment. + +- The `initialOwner` will then be allowed to call protected functions marked with the `onlyOwner` modifier.
diff --git a/docs/contracts/LSP6KeyManager/LSP6KeyManager.md b/docs/contracts/LSP6KeyManager/LSP6KeyManager.md index 514841938..78b00792d 100644 --- a/docs/contracts/LSP6KeyManager/LSP6KeyManager.md +++ b/docs/contracts/LSP6KeyManager/LSP6KeyManager.md @@ -1230,9 +1230,12 @@ function _nonReentrantBefore( ) internal nonpayable returns (bool reentrancyStatus); ``` -Update the status from `_NON_ENTERED` to `_ENTERED` and checks if -the status is `_ENTERED` in order to revert the call unless the caller has the REENTRANCY permission -Used in the beginning of the `nonReentrant` modifier, before the method execution starts. +Check if we are in the context of a reentrant call, by checking if the reentrancy status is `true`. + +- If the status is `true`, the caller (or signer for relay call) MUST have the `REENTRANCY` permission. Otherwise, the call is reverted. + +- If the status is `false`, it is set to `true` only if we are not dealing with a call to the functions `setData` or `setDataBatch`. + Used at the beginning of the [`lsp20VerifyCall`](#`lsp20verifycall`), [`_execute`](#`_execute`) and [`_executeRelayCall`](#`_executerelaycall`) functions, before the methods execution starts.
@@ -1242,8 +1245,8 @@ Used in the beginning of the `nonReentrant` modifier, before the method executio function _nonReentrantAfter(address targetContract) internal nonpayable; ``` -Resets the status to `false` -Used in the end of the `nonReentrant` modifier after the method execution is terminated +Resets the reentrancy status to `false` +Used at the end of the [`lsp20VerifyCall`](#`lsp20verifycall`), [`_execute`](#`_execute`) and [`_executeRelayCall`](#`_executerelaycall`) functions after the functions' execution is terminated.
diff --git a/docs/contracts/LSP9Vault/LSP9Vault.md b/docs/contracts/LSP9Vault/LSP9Vault.md index 087d8f578..f71c2d558 100644 --- a/docs/contracts/LSP9Vault/LSP9Vault.md +++ b/docs/contracts/LSP9Vault/LSP9Vault.md @@ -1180,7 +1180,7 @@ If there is an extension for the function selector being called, it calls the ex function _validateAndIdentifyCaller() internal view returns (bool isURD); ``` -Modifier restricting the call to the owner of the contract and the UniversalReceiverDelegate +Internal method restricting the call to the owner of the contract and the UniversalReceiverDelegate
diff --git a/docs/contracts/UniversalProfile.md b/docs/contracts/UniversalProfile.md index 0531b4ef2..f41fc0ddb 100644 --- a/docs/contracts/UniversalProfile.md +++ b/docs/contracts/UniversalProfile.md @@ -38,7 +38,11 @@ constructor(address initialOwner); _Deploying a UniversalProfile contract with owner set to address `initialOwner`._ -Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3UniversalProfile` data key in the ERC725Y data key/value store. The `constructor` also allows funding the contract on deployment. +Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3UniversalProfile` data key in the ERC725Y data key/value store. + +- The `constructor` is payable and allows funding the contract on deployment. + +- The `initialOwner` will then be allowed to call protected functions marked with the `onlyOwner` modifier.
From 0b27572bc14b6b34832371fdefe10a50a9471ec8 Mon Sep 17 00:00:00 2001 From: Yamen Merhi Date: Mon, 6 Nov 2023 16:05:56 +0200 Subject: [PATCH 18/22] refactor: add lsp1 data in ownership function hooks (#789) * refactor: add lsp1 data in ownership function hooks * test: resolve failing tests to latest behavior * refactor: add the hooks in LSP14 and LSP9 * test: resolve lsp1 failing tests * chore: delete renounceOwnershipStartedAt in acceptOwnership --------- Co-authored-by: Jean Cvllr <31145285+CJ42@users.noreply.github.com> --- .../LSP0ERC725AccountCore.sol | 13 +++++----- .../LSP14Ownable2Step/LSP14Ownable2Step.sol | 8 +++--- contracts/LSP9Vault/LSP9VaultCore.sol | 8 +++--- ...P1UniversalReceiverDelegateUP.behaviour.ts | 25 +++++++++++++++---- .../LSP20CallVerification.behaviour.ts | 6 ++++- tests/LSP9Vault/LSP9Vault.behaviour.ts | 5 +++- tests/UniversalProfile.behaviour.ts | 5 +++- 7 files changed, 48 insertions(+), 22 deletions(-) diff --git a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol index 2b436afbd..6a8f5ebac 100644 --- a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol +++ b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol @@ -543,7 +543,7 @@ abstract contract LSP0ERC725AccountCore is // notify the pending owner through LSP1 pendingNewOwner.notifyUniversalReceiver( _TYPEID_LSP0_OwnershipTransferStarted, - "" + abi.encode(currentOwner, pendingNewOwner) ); // reset the transfer ownership lock @@ -563,7 +563,7 @@ abstract contract LSP0ERC725AccountCore is // notify the pending owner through LSP1 pendingNewOwner.notifyUniversalReceiver( _TYPEID_LSP0_OwnershipTransferStarted, - "" + abi.encode(currentOwner, pendingNewOwner) ); // reset the transfer ownership lock @@ -598,6 +598,7 @@ abstract contract LSP0ERC725AccountCore is _setOwner(pendingOwnerAddress); delete _pendingOwner; + delete _renounceOwnershipStartedAt; } else { _acceptOwnership(); } @@ -605,13 +606,13 @@ abstract contract LSP0ERC725AccountCore is // notify the previous owner if supports LSP1 previousOwner.notifyUniversalReceiver( _TYPEID_LSP0_OwnershipTransferred_SenderNotification, - "" + abi.encode(previousOwner, pendingOwnerAddress) ); // notify the pending owner if supports LSP1 pendingOwnerAddress.notifyUniversalReceiver( _TYPEID_LSP0_OwnershipTransferred_RecipientNotification, - "" + abi.encode(previousOwner, pendingOwnerAddress) ); // If msg.sender != pendingOwnerAddress & verifyAfter is true, Call {lsp20VerifyCallResult} on the new owner @@ -644,7 +645,7 @@ abstract contract LSP0ERC725AccountCore is if (owner() == address(0)) { previousOwner.notifyUniversalReceiver( _TYPEID_LSP0_OwnershipTransferred_SenderNotification, - "" + abi.encode(accountOwner, address(0)) ); } } else { @@ -658,7 +659,7 @@ abstract contract LSP0ERC725AccountCore is if (owner() == address(0)) { previousOwner.notifyUniversalReceiver( _TYPEID_LSP0_OwnershipTransferred_SenderNotification, - "" + abi.encode(accountOwner, address(0)) ); } diff --git a/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol b/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol index a5a593803..561e27c83 100644 --- a/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol +++ b/contracts/LSP14Ownable2Step/LSP14Ownable2Step.sol @@ -99,7 +99,7 @@ abstract contract LSP14Ownable2Step is ILSP14Ownable2Step, OwnableUnset { newOwner.notifyUniversalReceiver( _TYPEID_LSP14_OwnershipTransferStarted, - "" + abi.encode(currentOwner, newOwner) ); // reset the transfer ownership lock @@ -118,12 +118,12 @@ abstract contract LSP14Ownable2Step is ILSP14Ownable2Step, OwnableUnset { previousOwner.notifyUniversalReceiver( _TYPEID_LSP14_OwnershipTransferred_SenderNotification, - "" + abi.encode(previousOwner, msg.sender) ); msg.sender.notifyUniversalReceiver( _TYPEID_LSP14_OwnershipTransferred_RecipientNotification, - "" + abi.encode(previousOwner, msg.sender) ); } @@ -144,7 +144,7 @@ abstract contract LSP14Ownable2Step is ILSP14Ownable2Step, OwnableUnset { if (owner() == address(0)) { previousOwner.notifyUniversalReceiver( _TYPEID_LSP14_OwnershipTransferred_SenderNotification, - "" + abi.encode(previousOwner, address(0)) ); } } diff --git a/contracts/LSP9Vault/LSP9VaultCore.sol b/contracts/LSP9Vault/LSP9VaultCore.sol index 0f1281117..6731325ea 100644 --- a/contracts/LSP9Vault/LSP9VaultCore.sol +++ b/contracts/LSP9Vault/LSP9VaultCore.sol @@ -440,7 +440,7 @@ contract LSP9VaultCore is newOwner.notifyUniversalReceiver( _TYPEID_LSP9_OwnershipTransferStarted, - "" + abi.encode(currentOwner, newOwner) ); // reset the transfer ownership lock @@ -462,12 +462,12 @@ contract LSP9VaultCore is previousOwner.notifyUniversalReceiver( _TYPEID_LSP9_OwnershipTransferred_SenderNotification, - "" + abi.encode(previousOwner, msg.sender) ); msg.sender.notifyUniversalReceiver( _TYPEID_LSP9_OwnershipTransferred_RecipientNotification, - "" + abi.encode(previousOwner, msg.sender) ); } @@ -490,7 +490,7 @@ contract LSP9VaultCore is if (owner() == address(0)) { previousOwner.notifyUniversalReceiver( _TYPEID_LSP9_OwnershipTransferred_SenderNotification, - "" + abi.encode(previousOwner, address(0)) ); } } diff --git a/tests/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP.behaviour.ts b/tests/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP.behaviour.ts index f5b241ff1..036d0e187 100644 --- a/tests/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP.behaviour.ts +++ b/tests/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP.behaviour.ts @@ -1573,7 +1573,10 @@ export const shouldBehaveLikeLSP1Delegate = ( vault.address, 0, LSP1_TYPE_IDS.LSP9OwnershipTransferred_SenderNotification, - '0x', + abiCoder.encode( + ['address', 'address'], + [context.universalProfile1.address, context.accounts.owner1.address], + ), lsp1ReturnedData, ); }); @@ -1649,7 +1652,10 @@ export const shouldBehaveLikeLSP1Delegate = ( vault.address, 0, LSP1_TYPE_IDS.LSP9OwnershipTransferred_SenderNotification, - '0x', + abiCoder.encode( + ['address', 'address'], + [context.universalProfile1.address, context.accounts.owner1.address], + ), lsp1ReturnedData, ); }); @@ -1732,7 +1738,10 @@ export const shouldBehaveLikeLSP1Delegate = ( vault.address, 0, LSP1_TYPE_IDS.LSP9OwnershipTransferred_SenderNotification, - '0x', + abiCoder.encode( + ['address', 'address'], + [context.universalProfile1.address, context.accounts.owner1.address], + ), lsp1ReturnedData, ); }); @@ -2882,7 +2891,10 @@ export const shouldBehaveLikeLSP1Delegate = ( vault.address, 0, LSP1_TYPE_IDS.LSP9OwnershipTransferred_SenderNotification, - '0x', + abiCoder.encode( + ['address', 'address'], + [testContext.universalProfile.address, newVaultOwner.address], + ), expectedReturnedValues, ); @@ -3004,7 +3016,10 @@ export const shouldBehaveLikeLSP1Delegate = ( vault.address, 0, LSP1_TYPE_IDS.LSP9OwnershipTransferred_RecipientNotification, - '0x', + abiCoder.encode( + ['address', 'address'], + [vaultOwner.address, context.universalProfile1.address], + ), expectedReturnedValues, ); diff --git a/tests/LSP20CallVerification/LSP20CallVerification.behaviour.ts b/tests/LSP20CallVerification/LSP20CallVerification.behaviour.ts index 3298975a8..712194e87 100644 --- a/tests/LSP20CallVerification/LSP20CallVerification.behaviour.ts +++ b/tests/LSP20CallVerification/LSP20CallVerification.behaviour.ts @@ -26,6 +26,7 @@ import { // constants import { LSP1_TYPE_IDS, LSP20_SUCCESS_VALUES, OPERATION_TYPES } from '../../constants'; +import { abiCoder } from './..//utils/helpers'; export type LSP20TestContext = { accounts: SignerWithAddress[]; @@ -237,7 +238,10 @@ export const shouldBehaveLikeLSP20 = (buildContext: () => Promise Date: Mon, 6 Nov 2023 17:17:48 +0000 Subject: [PATCH 19/22] refactor: use `VERSION` instead of `version` (#791) * build: add `Version` contract in artifact list * refactor: use `VERSION` as constant in LSP0, 1 Delegate, 6 and 9 * docs: update auto generated docs * fix: add missing `VERSION` in `UniversalProfileInit` * refactor: re-introduce `Version.sol` contract * fix: add missing `LSP17Extension` in inheritance of `OnERC721ReceivedExtension` --- .../LSP0ERC725Account/LSP0ERC725Account.sol | 4 +- .../LSP0ERC725AccountCore.sol | 2 - .../LSP0ERC725AccountInit.sol | 3 +- .../OnERC721ReceivedExtension.sol | 3 +- .../LSP1UniversalReceiverDelegateUP.sol | 2 +- .../LSP1UniversalReceiverDelegateVault.sol | 2 +- contracts/LSP6KeyManager/LSP6KeyManager.sol | 3 +- .../LSP6KeyManager/LSP6KeyManagerCore.sol | 2 - .../LSP6KeyManager/LSP6KeyManagerInit.sol | 3 +- .../LSP7DigitalAsset/LSP7DigitalAssetCore.sol | 3 +- .../LSP8IdentifiableDigitalAssetCore.sol | 6 +- contracts/LSP9Vault/LSP9Vault.sol | 3 +- contracts/LSP9Vault/LSP9VaultCore.sol | 2 - contracts/LSP9Vault/LSP9VaultInit.sol | 3 +- contracts/UniversalProfile.sol | 4 +- contracts/UniversalProfileInit.sol | 5 +- contracts/UniversalProfileInitAbstract.sol | 4 +- contracts/Version.sol | 16 ++-- .../LSP0ERC725Account/LSP0ERC725Account.md | 52 +++++------ .../LSP17ContractExtension/LSP17Extension.md | 48 +++++----- .../LSP17Extensions/Extension4337.md | 52 +++++------ .../OnERC721ReceivedExtension.md | 93 +++++++++++++++++++ .../LSP1UniversalReceiverDelegateUP.md | 52 +++++------ .../LSP1UniversalReceiverDelegateVault.md | 52 +++++------ .../LSP6KeyManager/LSP6KeyManager.md | 52 +++++------ .../LSP7DigitalAsset/LSP7DigitalAsset.md | 27 ------ .../extensions/LSP7Burnable.md | 27 ------ .../extensions/LSP7CappedSupply.md | 27 ------ .../extensions/LSP7CompatibleERC20.md | 27 ------ .../presets/LSP7CompatibleERC20Mintable.md | 27 ------ .../LSP7DigitalAsset/presets/LSP7Mintable.md | 27 ------ .../LSP8IdentifiableDigitalAsset.md | 27 ------ .../extensions/LSP8Burnable.md | 27 ------ .../extensions/LSP8CappedSupply.md | 27 ------ .../extensions/LSP8CompatibleERC721.md | 27 ------ .../extensions/LSP8Enumerable.md | 27 ------ .../presets/LSP8CompatibleERC721Mintable.md | 27 ------ .../presets/LSP8Mintable.md | 27 ------ docs/contracts/LSP9Vault/LSP9Vault.md | 52 +++++------ docs/contracts/UniversalProfile.md | 54 ++++++----- 40 files changed, 323 insertions(+), 605 deletions(-) diff --git a/contracts/LSP0ERC725Account/LSP0ERC725Account.sol b/contracts/LSP0ERC725Account/LSP0ERC725Account.sol index fbfaca060..10c006c94 100644 --- a/contracts/LSP0ERC725Account/LSP0ERC725Account.sol +++ b/contracts/LSP0ERC725Account/LSP0ERC725Account.sol @@ -2,13 +2,13 @@ pragma solidity ^0.8.4; // modules +import {Version} from "../Version.sol"; import {LSP0ERC725AccountCore} from "./LSP0ERC725AccountCore.sol"; import { OwnableUnset } from "@erc725/smart-contracts/contracts/custom/OwnableUnset.sol"; // constants - import {_TYPEID_LSP0_VALUE_RECEIVED} from "./LSP0Constants.sol"; /** @@ -26,7 +26,7 @@ import {_TYPEID_LSP0_VALUE_RECEIVED} from "./LSP0Constants.sol"; * - Extending the account with new functions and interfaceIds of future standards using [LSP-17-ContractExtension] * - Verifying calls on the owner to make it easier to interact with the account directly using [LSP-20-CallVerification] */ -contract LSP0ERC725Account is LSP0ERC725AccountCore { +contract LSP0ERC725Account is LSP0ERC725AccountCore, Version { /** * @notice Deploying a LSP0ERC725Account contract with owner set to address `initialOwner`. * diff --git a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol index 6a8f5ebac..4035228d6 100644 --- a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol +++ b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol @@ -23,7 +23,6 @@ import {LSP1Utils} from "../LSP1UniversalReceiver/LSP1Utils.sol"; import {LSP2Utils} from "../LSP2ERC725YJSONSchema/LSP2Utils.sol"; // modules -import {Version} from "../Version.sol"; import {ERC725YCore} from "@erc725/smart-contracts/contracts/ERC725YCore.sol"; import {ERC725XCore} from "@erc725/smart-contracts/contracts/ERC725XCore.sol"; import { @@ -78,7 +77,6 @@ import { abstract contract LSP0ERC725AccountCore is ERC725XCore, ERC725YCore, - Version, IERC1271, ILSP0ERC725Account, ILSP1UniversalReceiver, diff --git a/contracts/LSP0ERC725Account/LSP0ERC725AccountInit.sol b/contracts/LSP0ERC725Account/LSP0ERC725AccountInit.sol index bbbeaded1..c0fa0d2ce 100644 --- a/contracts/LSP0ERC725Account/LSP0ERC725AccountInit.sol +++ b/contracts/LSP0ERC725Account/LSP0ERC725AccountInit.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.4; // modules +import {Version} from "../Version.sol"; import { LSP0ERC725AccountInitAbstract } from "./LSP0ERC725AccountInitAbstract.sol"; @@ -21,7 +22,7 @@ import { * - Extending the account with new functions and interfaceIds of future standards using [LSP-17-ContractExtension] * - Verifying calls on the owner to make it easier to interact with the account directly using [LSP-20-CallVerification] */ -contract LSP0ERC725AccountInit is LSP0ERC725AccountInitAbstract { +contract LSP0ERC725AccountInit is LSP0ERC725AccountInitAbstract, Version { /** * @notice deploying a `LSP0ERC725AccountInit` base contract to be used behind proxy * @dev Locks the base contract on deployment, so that it cannot be initialized, owned and controlled by anyone after it has been deployed. This is intended so that the sole purpose of this contract is to be used as a base contract behind a proxy. diff --git a/contracts/LSP17Extensions/OnERC721ReceivedExtension.sol b/contracts/LSP17Extensions/OnERC721ReceivedExtension.sol index 47f6be65e..887c39802 100644 --- a/contracts/LSP17Extensions/OnERC721ReceivedExtension.sol +++ b/contracts/LSP17Extensions/OnERC721ReceivedExtension.sol @@ -4,12 +4,13 @@ pragma solidity ^0.8.4; import { ERC721Holder } from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; +import {LSP17Extension} from "../LSP17ContractExtension/LSP17Extension.sol"; /** * @dev LSP17 Extension that can be attached to a LSP17Extendable contract * to allow it to receive ERC721 tokens via `safeTransferFrom`. */ // solhint-disable-next-line no-empty-blocks -contract OnERC721ReceivedExtension is ERC721Holder { +contract OnERC721ReceivedExtension is ERC721Holder, LSP17Extension { } diff --git a/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol b/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol index 6e8c236dd..d2b82a414 100644 --- a/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol +++ b/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol @@ -11,8 +11,8 @@ import { import {ILSP7DigitalAsset} from "../../LSP7DigitalAsset/ILSP7DigitalAsset.sol"; // modules -import {Version} from "../../Version.sol"; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {Version} from "../../Version.sol"; // libraries import { diff --git a/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol b/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol index 097d56453..1e0f3eb67 100644 --- a/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol +++ b/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol @@ -11,8 +11,8 @@ import { import {ILSP7DigitalAsset} from "../../LSP7DigitalAsset/ILSP7DigitalAsset.sol"; // modules -import {Version} from "../../Version.sol"; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {Version} from "../../Version.sol"; // libraries import {LSP5Utils} from "../../LSP5ReceivedAssets/LSP5Utils.sol"; diff --git a/contracts/LSP6KeyManager/LSP6KeyManager.sol b/contracts/LSP6KeyManager/LSP6KeyManager.sol index 6e9e9cc32..f30ce78de 100644 --- a/contracts/LSP6KeyManager/LSP6KeyManager.sol +++ b/contracts/LSP6KeyManager/LSP6KeyManager.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.5; // modules +import {Version} from "../Version.sol"; import {LSP6KeyManagerCore} from "./LSP6KeyManagerCore.sol"; import {InvalidLSP6Target} from "./LSP6Errors.sol"; @@ -10,7 +11,7 @@ import {InvalidLSP6Target} from "./LSP6Errors.sol"; * @author Fabian Vogelsteller , Jean Cavallera (CJ42), Yamen Merhi (YamenMerhi) * @dev All the permissions can be set on the ERC725 Account using `setData(bytes32,bytes)` or `setData(bytes32[],bytes[])`. */ -contract LSP6KeyManager is LSP6KeyManagerCore { +contract LSP6KeyManager is LSP6KeyManagerCore, Version { /** * @notice Deploying a LSP6KeyManager linked to the contract at address `target_`. * @dev Deploy a Key Manager and set the `target_` address in the contract storage, diff --git a/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol b/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol index 96a129091..b4dae6dcb 100644 --- a/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol +++ b/contracts/LSP6KeyManager/LSP6KeyManagerCore.sol @@ -18,7 +18,6 @@ import { } from "../LSP25ExecuteRelayCall/ILSP25ExecuteRelayCall.sol"; // modules -import {Version} from "../Version.sol"; import {ILSP14Ownable2Step} from "../LSP14Ownable2Step/ILSP14Ownable2Step.sol"; import {ERC725Y} from "@erc725/smart-contracts/contracts/ERC725Y.sol"; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; @@ -88,7 +87,6 @@ abstract contract LSP6KeyManagerCore is ILSP6, ILSP20, ILSP25, - Version, LSP6SetDataModule, LSP6ExecuteModule, LSP6ExecuteRelayCallModule, diff --git a/contracts/LSP6KeyManager/LSP6KeyManagerInit.sol b/contracts/LSP6KeyManager/LSP6KeyManagerInit.sol index dc55b8fdd..92570729b 100644 --- a/contracts/LSP6KeyManager/LSP6KeyManagerInit.sol +++ b/contracts/LSP6KeyManager/LSP6KeyManagerInit.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.5; // modules +import {Version} from "../Version.sol"; import {LSP6KeyManagerInitAbstract} from "./LSP6KeyManagerInitAbstract.sol"; /** @@ -9,7 +10,7 @@ import {LSP6KeyManagerInitAbstract} from "./LSP6KeyManagerInitAbstract.sol"; * @author Fabian Vogelsteller , Jean Cavallera (CJ42), Yamen Merhi (YamenMerhi) * @dev All the permissions can be set on the ERC725 Account using `setData(...)` with the keys constants below */ -contract LSP6KeyManagerInit is LSP6KeyManagerInitAbstract { +contract LSP6KeyManagerInit is LSP6KeyManagerInitAbstract, Version { /** * @notice Deploying a LSP6KeyManagerInit to be used as base contract behind proxy. * @dev Initialize (= lock) base implementation contract on deployment. diff --git a/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol b/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol index 621fc66a6..56418c3f3 100644 --- a/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol +++ b/contracts/LSP7DigitalAsset/LSP7DigitalAssetCore.sol @@ -8,7 +8,6 @@ import { import {ILSP7DigitalAsset} from "./ILSP7DigitalAsset.sol"; // modules -import {Version} from "../Version.sol"; // libraries import { @@ -52,7 +51,7 @@ import { * Similar to ERC20, the non-standard {increaseAllowance} and {decreaseAllowance} functions * have been added to mitigate the well-known issues around setting allowances. */ -abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset, Version { +abstract contract LSP7DigitalAssetCore is ILSP7DigitalAsset { using EnumerableSet for EnumerableSet.AddressSet; using ERC165Checker for address; using LSP1Utils for address; diff --git a/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetCore.sol b/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetCore.sol index 5443f6a14..48ceb4a91 100644 --- a/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetCore.sol +++ b/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAssetCore.sol @@ -9,9 +9,6 @@ import { ILSP8IdentifiableDigitalAsset } from "./ILSP8IdentifiableDigitalAsset.sol"; -// modules -import {Version} from "../Version.sol"; - // libraries import { EnumerableSet @@ -52,8 +49,7 @@ import { * @dev Core Implementation of a LSP8 compliant contract. */ abstract contract LSP8IdentifiableDigitalAssetCore is - ILSP8IdentifiableDigitalAsset, - Version + ILSP8IdentifiableDigitalAsset { using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.Bytes32Set; diff --git a/contracts/LSP9Vault/LSP9Vault.sol b/contracts/LSP9Vault/LSP9Vault.sol index 4a547ab99..7fa70ce43 100644 --- a/contracts/LSP9Vault/LSP9Vault.sol +++ b/contracts/LSP9Vault/LSP9Vault.sol @@ -5,6 +5,7 @@ pragma solidity ^0.8.4; import { OwnableUnset } from "@erc725/smart-contracts/contracts/custom/OwnableUnset.sol"; +import {Version} from "../Version.sol"; import {LSP9VaultCore} from "./LSP9VaultCore.sol"; // libraries @@ -23,7 +24,7 @@ import { * @author Fabian Vogelsteller, Yamen Merhi, Jean Cavallera * @dev Could be owned by an EOA or by a contract and is able to receive and send assets. Also allows for registering received assets by leveraging the key-value storage. */ -contract LSP9Vault is LSP9VaultCore { +contract LSP9Vault is LSP9VaultCore, Version { using LSP1Utils for address; /** diff --git a/contracts/LSP9Vault/LSP9VaultCore.sol b/contracts/LSP9Vault/LSP9VaultCore.sol index 6731325ea..db3b333ef 100644 --- a/contracts/LSP9Vault/LSP9VaultCore.sol +++ b/contracts/LSP9Vault/LSP9VaultCore.sol @@ -20,7 +20,6 @@ import {LSP1Utils} from "../LSP1UniversalReceiver/LSP1Utils.sol"; import {LSP2Utils} from "../LSP2ERC725YJSONSchema/LSP2Utils.sol"; // modules -import {Version} from "../Version.sol"; import {ERC725XCore} from "@erc725/smart-contracts/contracts/ERC725XCore.sol"; import {ERC725YCore} from "@erc725/smart-contracts/contracts/ERC725YCore.sol"; import { @@ -81,7 +80,6 @@ import { contract LSP9VaultCore is ERC725XCore, ERC725YCore, - Version, LSP14Ownable2Step, LSP17Extendable, ILSP1UniversalReceiver, diff --git a/contracts/LSP9Vault/LSP9VaultInit.sol b/contracts/LSP9Vault/LSP9VaultInit.sol index 2548d09d8..427275349 100644 --- a/contracts/LSP9Vault/LSP9VaultInit.sol +++ b/contracts/LSP9Vault/LSP9VaultInit.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.4; // modules +import {Version} from "../Version.sol"; import {LSP9VaultInitAbstract} from "./LSP9VaultInitAbstract.sol"; /** @@ -9,7 +10,7 @@ import {LSP9VaultInitAbstract} from "./LSP9VaultInitAbstract.sol"; * @author Fabian Vogelsteller, Yamen Merhi, Jean Cavallera * @dev Could be owned by a UniversalProfile and able to register received asset with UniversalReceiverDelegateVault */ -contract LSP9VaultInit is LSP9VaultInitAbstract { +contract LSP9VaultInit is LSP9VaultInitAbstract, Version { /** * @dev initialize (= lock) base implementation contract on deployment */ diff --git a/contracts/UniversalProfile.sol b/contracts/UniversalProfile.sol index fddcc5434..027c5cd34 100644 --- a/contracts/UniversalProfile.sol +++ b/contracts/UniversalProfile.sol @@ -19,7 +19,7 @@ contract UniversalProfile is LSP0ERC725Account { /** * @notice Deploying a UniversalProfile contract with owner set to address `initialOwner`. * - * @dev Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3UniversalProfile` data key in the ERC725Y data key/value store. + * @dev Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3Profile` data key in the ERC725Y data key/value store. * - The `constructor` is payable and allows funding the contract on deployment. * - The `initialOwner` will then be allowed to call protected functions marked with the `onlyOwner` modifier. * @@ -31,7 +31,7 @@ contract UniversalProfile is LSP0ERC725Account { * - {DataChanged} event when setting the {_LSP3_SUPPORTED_STANDARDS_KEY}. */ constructor(address initialOwner) payable LSP0ERC725Account(initialOwner) { - // set data key SupportedStandards:LSP3UniversalProfile + // set data key SupportedStandards:LSP3Profile _setData( _LSP3_SUPPORTED_STANDARDS_KEY, _LSP3_SUPPORTED_STANDARDS_VALUE diff --git a/contracts/UniversalProfileInit.sol b/contracts/UniversalProfileInit.sol index 38c9bbf1d..bcac3b039 100644 --- a/contracts/UniversalProfileInit.sol +++ b/contracts/UniversalProfileInit.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.4; // modules +import {Version} from "./Version.sol"; import {UniversalProfileInitAbstract} from "./UniversalProfileInitAbstract.sol"; /** @@ -9,7 +10,7 @@ import {UniversalProfileInitAbstract} from "./UniversalProfileInitAbstract.sol"; * @author Fabian Vogelsteller * @dev Implementation of the ERC725Account + LSP1 universalReceiver */ -contract UniversalProfileInit is UniversalProfileInitAbstract { +contract UniversalProfileInit is UniversalProfileInitAbstract, Version { /** * @notice deploying a `UniversalProfileInit` base contract to be used behind proxy * @dev Locks the base contract on deployment, so that it cannot be initialized, owned and controlled by anyone after it has been deployed. @@ -22,7 +23,7 @@ contract UniversalProfileInit is UniversalProfileInitAbstract { /** * @notice Initializing a UniversalProfile contract with owner set to address `initialOwner`. * - * @dev Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3UniversalProfile` data key in the ERC725Y data key/value store. + * @dev Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3Profile` data key in the ERC725Y data key/value store. * - The `initialize(address)` function is payable and allows funding the contract on initialization. * - The `initialOwner` will then be allowed to call protected functions marked with the `onlyOwner` modifier. * diff --git a/contracts/UniversalProfileInitAbstract.sol b/contracts/UniversalProfileInitAbstract.sol index b6f39003c..f49f14203 100644 --- a/contracts/UniversalProfileInitAbstract.sol +++ b/contracts/UniversalProfileInitAbstract.sol @@ -21,7 +21,7 @@ abstract contract UniversalProfileInitAbstract is LSP0ERC725AccountInitAbstract { /** - * @dev Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3UniversalProfile` data key in the ERC725Y data key/value store. + * @dev Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3Profile` data key in the ERC725Y data key/value store. * The `initialOwner` will then be allowed to call protected functions marked with the `onlyOwner` modifier. * * @param initialOwner The owner of the contract. @@ -38,7 +38,7 @@ abstract contract UniversalProfileInitAbstract is ) internal virtual override onlyInitializing { LSP0ERC725AccountInitAbstract._initialize(initialOwner); - // set data key SupportedStandards:LSP3UniversalProfile + // set data key SupportedStandards:LSP3Profile _setData( _LSP3_SUPPORTED_STANDARDS_KEY, _LSP3_SUPPORTED_STANDARDS_VALUE diff --git a/contracts/Version.sol b/contracts/Version.sol index f0a27b093..2b954c6bc 100644 --- a/contracts/Version.sol +++ b/contracts/Version.sol @@ -2,20 +2,16 @@ pragma solidity ^0.8.4; abstract contract Version { - // DO NOT CHANGE - // Comments block below is used by release-please to automatically update the version in this file. - // x-release-please-start-version - string internal constant _VERSION = "0.12.0"; - - // x-release-please-end - /** * @dev Get the version of the contract. * @notice Contract version. * * @return The version of the the contract. */ - function version() public view virtual returns (string memory) { - return _VERSION; - } + // DO NOT CHANGE + // Comments block below is used by release-please to automatically update the version in this file. + // x-release-please-start-version + string public constant VERSION = "0.12.0"; + + // x-release-please-end } diff --git a/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md b/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md index adaa4c9e8..a4393004b 100644 --- a/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md +++ b/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md @@ -201,6 +201,31 @@ function RENOUNCE_OWNERSHIP_CONFIRMATION_PERIOD()
+### VERSION + +:::note References + +- Specification details: [**LSP-0-ERC725Account**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-0-ERC725Account.md#version) +- Solidity implementation: [`LSP0ERC725Account.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP0ERC725Account/LSP0ERC725Account.sol) +- Function signature: `VERSION()` +- Function selector: `0xffa1ad74` + +::: + +```solidity +function VERSION() external view returns (string); +``` + +_Contract version._ + +#### Returns + +| Name | Type | Description | +| ---- | :------: | ----------- | +| `0` | `string` | - | + +
+ ### acceptOwnership :::note References @@ -848,33 +873,6 @@ Achieves the goal of [LSP-1-UniversalReceiver] by allowing the account to be not
-### version - -:::note References - -- Specification details: [**LSP-0-ERC725Account**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-0-ERC725Account.md#version) -- Solidity implementation: [`LSP0ERC725Account.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP0ERC725Account/LSP0ERC725Account.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP17ContractExtension/LSP17Extension.md b/docs/contracts/LSP17ContractExtension/LSP17Extension.md index fe023c4dc..9206a06c9 100644 --- a/docs/contracts/LSP17ContractExtension/LSP17Extension.md +++ b/docs/contracts/LSP17ContractExtension/LSP17Extension.md @@ -23,61 +23,59 @@ Implementation of the extension logic according to LSP17ContractExtension. This Public methods are accessible externally from users, allowing interaction with this function from dApps or other smart contracts. When marked as 'public', a method can be called both externally and internally, on the other hand, when marked as 'external', a method can only be called externally. -### supportsInterface +### VERSION :::note References -- Specification details: [**LSP-17-ContractExtension**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-ContractExtension.md#supportsinterface) +- Specification details: [**LSP-17-ContractExtension**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-ContractExtension.md#version) - Solidity implementation: [`LSP17Extension.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP17ContractExtension/LSP17Extension.sol) -- Function signature: `supportsInterface(bytes4)` -- Function selector: `0x01ffc9a7` +- Function signature: `VERSION()` +- Function selector: `0xffa1ad74` ::: ```solidity -function supportsInterface(bytes4 interfaceId) external view returns (bool); +function VERSION() external view returns (string); ``` -See [`IERC165-supportsInterface`](#ierc165-supportsinterface). - -#### Parameters - -| Name | Type | Description | -| ------------- | :------: | ----------- | -| `interfaceId` | `bytes4` | - | +_Contract version._ #### Returns -| Name | Type | Description | -| ---- | :----: | ----------- | -| `0` | `bool` | - | +| Name | Type | Description | +| ---- | :------: | ----------- | +| `0` | `string` | - |
-### version +### supportsInterface :::note References -- Specification details: [**LSP-17-ContractExtension**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-ContractExtension.md#version) +- Specification details: [**LSP-17-ContractExtension**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-ContractExtension.md#supportsinterface) - Solidity implementation: [`LSP17Extension.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP17ContractExtension/LSP17Extension.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` +- Function signature: `supportsInterface(bytes4)` +- Function selector: `0x01ffc9a7` ::: ```solidity -function version() external view returns (string); +function supportsInterface(bytes4 interfaceId) external view returns (bool); ``` -_Contract version._ +See [`IERC165-supportsInterface`](#ierc165-supportsinterface). -Get the version of the contract. +#### Parameters + +| Name | Type | Description | +| ------------- | :------: | ----------- | +| `interfaceId` | `bytes4` | - | #### Returns -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | +| Name | Type | Description | +| ---- | :----: | ----------- | +| `0` | `bool` | - |
diff --git a/docs/contracts/LSP17Extensions/Extension4337.md b/docs/contracts/LSP17Extensions/Extension4337.md index bbb7be43d..75f90750f 100644 --- a/docs/contracts/LSP17Extensions/Extension4337.md +++ b/docs/contracts/LSP17Extensions/Extension4337.md @@ -40,6 +40,31 @@ constructor(address entryPoint_);
+### VERSION + +:::note References + +- Specification details: [**LSP-17-Extensions**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-Extensions.md#version) +- Solidity implementation: [`Extension4337.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP17Extensions/Extension4337.sol) +- Function signature: `VERSION()` +- Function selector: `0xffa1ad74` + +::: + +```solidity +function VERSION() external view returns (string); +``` + +_Contract version._ + +#### Returns + +| Name | Type | Description | +| ---- | :------: | ----------- | +| `0` | `string` | - | + +
+ ### entryPoint :::note References @@ -135,33 +160,6 @@ Must validate caller is the entryPoint. Must validate the signature and nonce
-### version - -:::note References - -- Specification details: [**LSP-17-Extensions**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-Extensions.md#version) -- Solidity implementation: [`Extension4337.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP17Extensions/Extension4337.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP17Extensions/OnERC721ReceivedExtension.md b/docs/contracts/LSP17Extensions/OnERC721ReceivedExtension.md index 1f504f9a8..bd763aff9 100644 --- a/docs/contracts/LSP17Extensions/OnERC721ReceivedExtension.md +++ b/docs/contracts/LSP17Extensions/OnERC721ReceivedExtension.md @@ -21,6 +21,31 @@ LSP17 Extension that can be attached to a LSP17Extendable contract to allow it t Public methods are accessible externally from users, allowing interaction with this function from dApps or other smart contracts. When marked as 'public', a method can be called both externally and internally, on the other hand, when marked as 'external', a method can only be called externally. +### VERSION + +:::note References + +- Specification details: [**LSP-17-Extensions**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-Extensions.md#version) +- Solidity implementation: [`OnERC721ReceivedExtension.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP17Extensions/OnERC721ReceivedExtension.sol) +- Function signature: `VERSION()` +- Function selector: `0xffa1ad74` + +::: + +```solidity +function VERSION() external view returns (string); +``` + +_Contract version._ + +#### Returns + +| Name | Type | Description | +| ---- | :------: | ----------- | +| `0` | `string` | - | + +
+ ### onERC721Received :::note References @@ -59,3 +84,71 @@ See [`IERC721Receiver-onERC721Received`](#ierc721receiver-onerc721received). Alw | `0` | `bytes4` | - |
+ +### supportsInterface + +:::note References + +- Specification details: [**LSP-17-Extensions**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-17-Extensions.md#supportsinterface) +- Solidity implementation: [`OnERC721ReceivedExtension.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP17Extensions/OnERC721ReceivedExtension.sol) +- Function signature: `supportsInterface(bytes4)` +- Function selector: `0x01ffc9a7` + +::: + +```solidity +function supportsInterface(bytes4 interfaceId) external view returns (bool); +``` + +See [`IERC165-supportsInterface`](#ierc165-supportsinterface). + +#### Parameters + +| Name | Type | Description | +| ------------- | :------: | ----------- | +| `interfaceId` | `bytes4` | - | + +#### Returns + +| Name | Type | Description | +| ---- | :----: | ----------- | +| `0` | `bool` | - | + +
+ +## Internal Methods + +Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. + +Internal functions cannot be called externally, whether from other smart contracts, dApp interfaces, or backend services. Their restricted accessibility ensures that they remain exclusively available within the context of the current contract, promoting controlled and encapsulated usage of these internal utilities. + +### \_extendableMsgData + +```solidity +function _extendableMsgData() internal view returns (bytes); +``` + +Returns the original `msg.data` passed to the extendable contract +without the appended `msg.sender` and `msg.value`. + +
+ +### \_extendableMsgSender + +```solidity +function _extendableMsgSender() internal view returns (address); +``` + +Returns the original `msg.sender` calling the extendable contract. + +
+ +### \_extendableMsgValue + +```solidity +function _extendableMsgValue() internal view returns (uint256); +``` + +Returns the original `msg.value` sent to the extendable contract. + +
diff --git a/docs/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.md b/docs/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.md index af880d4a2..fe770703b 100644 --- a/docs/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.md +++ b/docs/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.md @@ -27,6 +27,31 @@ The [`LSP1UniversalReceiverDelegateUP`](#lsp1universalreceiverdelegateup) follow Public methods are accessible externally from users, allowing interaction with this function from dApps or other smart contracts. When marked as 'public', a method can be called both externally and internally, on the other hand, when marked as 'external', a method can only be called externally. +### VERSION + +:::note References + +- Specification details: [**LSP-1-UniversalReceiver**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-1-UniversalReceiver.md#version) +- Solidity implementation: [`LSP1UniversalReceiverDelegateUP.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol) +- Function signature: `VERSION()` +- Function selector: `0xffa1ad74` + +::: + +```solidity +function VERSION() external view returns (string); +``` + +_Contract version._ + +#### Returns + +| Name | Type | Description | +| ---- | :------: | ----------- | +| `0` | `string` | - | + +
+ ### supportsInterface :::note References @@ -123,33 +148,6 @@ _Reacted on received notification with `typeId`._
-### version - -:::note References - -- Specification details: [**LSP-1-UniversalReceiver**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-1-UniversalReceiver.md#version) -- Solidity implementation: [`LSP1UniversalReceiverDelegateUP.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.md b/docs/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.md index 609078570..368b82286 100644 --- a/docs/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.md +++ b/docs/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.md @@ -25,6 +25,31 @@ The [`LSP1UniversalReceiverDelegateVault`](#lsp1universalreceiverdelegatevault) Public methods are accessible externally from users, allowing interaction with this function from dApps or other smart contracts. When marked as 'public', a method can be called both externally and internally, on the other hand, when marked as 'external', a method can only be called externally. +### VERSION + +:::note References + +- Specification details: [**LSP-1-UniversalReceiver**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-1-UniversalReceiver.md#version) +- Solidity implementation: [`LSP1UniversalReceiverDelegateVault.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol) +- Function signature: `VERSION()` +- Function selector: `0xffa1ad74` + +::: + +```solidity +function VERSION() external view returns (string); +``` + +_Contract version._ + +#### Returns + +| Name | Type | Description | +| ---- | :------: | ----------- | +| `0` | `string` | - | + +
+ ### supportsInterface :::note References @@ -112,33 +137,6 @@ Handles two cases: Writes the received [LSP-7-DigitalAsset] or [LSP-8-Identifiab
-### version - -:::note References - -- Specification details: [**LSP-1-UniversalReceiver**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-1-UniversalReceiver.md#version) -- Solidity implementation: [`LSP1UniversalReceiverDelegateVault.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateVault/LSP1UniversalReceiverDelegateVault.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP6KeyManager/LSP6KeyManager.md b/docs/contracts/LSP6KeyManager/LSP6KeyManager.md index 78b00792d..9812844fc 100644 --- a/docs/contracts/LSP6KeyManager/LSP6KeyManager.md +++ b/docs/contracts/LSP6KeyManager/LSP6KeyManager.md @@ -48,6 +48,31 @@ Deploy a Key Manager and set the `target_` address in the contract storage, maki
+### VERSION + +:::note References + +- Specification details: [**LSP-6-KeyManager**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-6-KeyManager.md#version) +- Solidity implementation: [`LSP6KeyManager.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP6KeyManager/LSP6KeyManager.sol) +- Function signature: `VERSION()` +- Function selector: `0xffa1ad74` + +::: + +```solidity +function VERSION() external view returns (string); +``` + +_Contract version._ + +#### Returns + +| Name | Type | Description | +| ---- | :------: | ----------- | +| `0` | `string` | - | + +
+ ### execute :::note References @@ -469,33 +494,6 @@ Get The address of the contract linked to this Key Manager.
-### version - -:::note References - -- Specification details: [**LSP-6-KeyManager**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-6-KeyManager.md#version) -- Solidity implementation: [`LSP6KeyManager.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP6KeyManager/LSP6KeyManager.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md b/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md index 02025db4c..5d22232a4 100644 --- a/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md +++ b/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md @@ -696,33 +696,6 @@ Transfers ownership of the contract to a new account (`newOwner`). Can only be c
-### version - -:::note References - -- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#version) -- Solidity implementation: [`LSP7DigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md b/docs/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md index 28572334b..a2001b08c 100644 --- a/docs/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md +++ b/docs/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md @@ -721,33 +721,6 @@ Transfers ownership of the contract to a new account (`newOwner`). Can only be c
-### version - -:::note References - -- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#version) -- Solidity implementation: [`LSP7Burnable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md b/docs/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md index 38ca3799e..9a6ee10de 100644 --- a/docs/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md +++ b/docs/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md @@ -721,33 +721,6 @@ Transfers ownership of the contract to a new account (`newOwner`). Can only be c
-### version - -:::note References - -- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#version) -- Solidity implementation: [`LSP7CappedSupply.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.md b/docs/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.md index 49e6a5ceb..e2590db3c 100644 --- a/docs/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.md +++ b/docs/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.md @@ -898,33 +898,6 @@ Transfers ownership of the contract to a new account (`newOwner`). Can only be c
-### version - -:::note References - -- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#version) -- Solidity implementation: [`LSP7CompatibleERC20.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.md b/docs/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.md index 0abc22ff6..5bfe41f4d 100644 --- a/docs/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.md +++ b/docs/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.md @@ -932,33 +932,6 @@ Transfers ownership of the contract to a new account (`newOwner`). Can only be c
-### version - -:::note References - -- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#version) -- Solidity implementation: [`LSP7CompatibleERC20Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP7DigitalAsset/presets/LSP7Mintable.md b/docs/contracts/LSP7DigitalAsset/presets/LSP7Mintable.md index 5bafb806f..a82ed62e5 100644 --- a/docs/contracts/LSP7DigitalAsset/presets/LSP7Mintable.md +++ b/docs/contracts/LSP7DigitalAsset/presets/LSP7Mintable.md @@ -758,33 +758,6 @@ Transfers ownership of the contract to a new account (`newOwner`). Can only be c
-### version - -:::note References - -- Specification details: [**LSP-7-DigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-7-DigitalAsset.md#version) -- Solidity implementation: [`LSP7Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP7DigitalAsset/presets/LSP7Mintable.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md b/docs/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md index c3c5c282d..36458f18d 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md @@ -663,33 +663,6 @@ Transfers ownership of the contract to a new account (`newOwner`). Can only be c
-### version - -:::note References - -- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#version) -- Solidity implementation: [`LSP8IdentifiableDigitalAsset.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md index 9b444005b..c472f56dd 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md @@ -689,33 +689,6 @@ Transfers ownership of the contract to a new account (`newOwner`). Can only be c
-### version - -:::note References - -- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#version) -- Solidity implementation: [`LSP8Burnable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md index a2688e0ef..50a1f2cb0 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md @@ -688,33 +688,6 @@ Transfers ownership of the contract to a new account (`newOwner`). Can only be c
-### version - -:::note References - -- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#version) -- Solidity implementation: [`LSP8CappedSupply.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.md b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.md index a23d9573d..080a70108 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.md @@ -1039,33 +1039,6 @@ Transfers ownership of the contract to a new account (`newOwner`). Can only be c
-### version - -:::note References - -- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#version) -- Solidity implementation: [`LSP8CompatibleERC721.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md index a9dec5aa0..41cde5627 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md @@ -694,33 +694,6 @@ Transfers ownership of the contract to a new account (`newOwner`). Can only be c
-### version - -:::note References - -- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#version) -- Solidity implementation: [`LSP8Enumerable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.md b/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.md index 4c33c7597..6b3867ab6 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.md @@ -1081,33 +1081,6 @@ Transfers ownership of the contract to a new account (`newOwner`). Can only be c
-### version - -:::note References - -- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#version) -- Solidity implementation: [`LSP8CompatibleERC721Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.md b/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.md index 8308d8036..a1993aa81 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.md @@ -727,33 +727,6 @@ Transfers ownership of the contract to a new account (`newOwner`). Can only be c
-### version - -:::note References - -- Specification details: [**LSP-8-IdentifiableDigitalAsset**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-8-IdentifiableDigitalAsset.md#version) -- Solidity implementation: [`LSP8Mintable.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/LSP9Vault/LSP9Vault.md b/docs/contracts/LSP9Vault/LSP9Vault.md index f71c2d558..54603da9d 100644 --- a/docs/contracts/LSP9Vault/LSP9Vault.md +++ b/docs/contracts/LSP9Vault/LSP9Vault.md @@ -183,6 +183,31 @@ function RENOUNCE_OWNERSHIP_CONFIRMATION_PERIOD()
+### VERSION + +:::note References + +- Specification details: [**LSP-9-Vault**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-9-Vault.md#version) +- Solidity implementation: [`LSP9Vault.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP9Vault/LSP9Vault.sol) +- Function signature: `VERSION()` +- Function selector: `0xffa1ad74` + +::: + +```solidity +function VERSION() external view returns (string); +``` + +_Contract version._ + +#### Returns + +| Name | Type | Description | +| ---- | :------: | ----------- | +| `0` | `string` | - | + +
+ ### acceptOwnership :::note References @@ -778,33 +803,6 @@ Achieves the goal of [LSP-1-UniversalReceiver] by allowing the account to be not
-### version - -:::note References - -- Specification details: [**LSP-9-Vault**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-9-Vault.md#version) -- Solidity implementation: [`LSP9Vault.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/LSP9Vault/LSP9Vault.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. diff --git a/docs/contracts/UniversalProfile.md b/docs/contracts/UniversalProfile.md index f41fc0ddb..f2003f5bc 100644 --- a/docs/contracts/UniversalProfile.md +++ b/docs/contracts/UniversalProfile.md @@ -38,7 +38,7 @@ constructor(address initialOwner); _Deploying a UniversalProfile contract with owner set to address `initialOwner`._ -Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3UniversalProfile` data key in the ERC725Y data key/value store. +Set `initialOwner` as the contract owner and the `SupportedStandards:LSP3Profile` data key in the ERC725Y data key/value store. - The `constructor` is payable and allows funding the contract on deployment. @@ -144,6 +144,31 @@ function RENOUNCE_OWNERSHIP_CONFIRMATION_PERIOD()
+### VERSION + +:::note References + +- Specification details: [**UniversalProfile**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-3-UniversalProfile-Metadata.md#version) +- Solidity implementation: [`UniversalProfile.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/UniversalProfile.sol) +- Function signature: `VERSION()` +- Function selector: `0xffa1ad74` + +::: + +```solidity +function VERSION() external view returns (string); +``` + +_Contract version._ + +#### Returns + +| Name | Type | Description | +| ---- | :------: | ----------- | +| `0` | `string` | - | + +
+ ### acceptOwnership :::note References @@ -791,33 +816,6 @@ Achieves the goal of [LSP-1-UniversalReceiver] by allowing the account to be not
-### version - -:::note References - -- Specification details: [**UniversalProfile**](https://github.com/lukso-network/lips/tree/main/LSPs/LSP-3-UniversalProfile-Metadata.md#version) -- Solidity implementation: [`UniversalProfile.sol`](https://github.com/lukso-network/lsp-smart-contracts/blob/develop/contracts/UniversalProfile.sol) -- Function signature: `version()` -- Function selector: `0x54fd4d50` - -::: - -```solidity -function version() external view returns (string); -``` - -_Contract version._ - -Get the version of the contract. - -#### Returns - -| Name | Type | Description | -| ---- | :------: | -------------------------------- | -| `0` | `string` | The version of the the contract. | - -
- ## Internal Methods Any method labeled as `internal` serves as utility function within the contract. They can be used when writing solidity contracts that inherit from this contract. These methods can be extended or modified by overriding their internal behavior to suit specific needs. From d3f26ea365d933cbf4c6dce2739f643b9bf6b75a Mon Sep 17 00:00:00 2001 From: "b00ste.lyx" <62855857+b00ste@users.noreply.github.com> Date: Mon, 6 Nov 2023 19:48:06 +0200 Subject: [PATCH 20/22] refactor: generate local links for `@param` & `@return` (#793) * refactor: local links for `@param` & `@return` * docs: update auto-generated docs --- .../LSP0ERC725Account/LSP0ERC725Account.md | 8 +- .../LSP25MultiChannelNonce.md | 14 +-- .../LSP6KeyManager/LSP6KeyManager.md | 110 +++++++++--------- .../LSP7DigitalAsset/LSP7DigitalAsset.md | 14 +-- .../extensions/LSP7Burnable.md | 14 +-- .../extensions/LSP7CappedSupply.md | 2 +- .../extensions/LSP7CompatibleERC20.md | 2 +- .../presets/LSP7CompatibleERC20Mintable.md | 2 +- .../LSP7DigitalAsset/presets/LSP7Mintable.md | 14 +-- .../LSP8IdentifiableDigitalAsset.md | 14 +-- .../extensions/LSP8Burnable.md | 14 +-- .../extensions/LSP8CappedSupply.md | 14 +-- .../extensions/LSP8CompatibleERC721.md | 14 +-- .../extensions/LSP8Enumerable.md | 14 +-- .../presets/LSP8CompatibleERC721Mintable.md | 14 +-- .../presets/LSP8Mintable.md | 14 +-- docs/contracts/LSP9Vault/LSP9Vault.md | 8 +- docs/contracts/UniversalProfile.md | 8 +- dodoc/config.ts | 2 +- 19 files changed, 148 insertions(+), 148 deletions(-) diff --git a/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md b/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md index a4393004b..9a2dd2b6c 100644 --- a/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md +++ b/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md @@ -1472,11 +1472,11 @@ Emitted when the [`universalReceiver`](#universalreceiver) function was called w | Name | Type | Description | | ---------------------- | :-------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `from` **`indexed`** | `address` | The address of the EOA or smart contract that called the {universalReceiver(...)} function. | -| `value` **`indexed`** | `uint256` | The amount sent to the {universalReceiver(...)} function. | +| `from` **`indexed`** | `address` | The address of the EOA or smart contract that called the [`universalReceiver(...)`](#universalreceiver) function. | +| `value` **`indexed`** | `uint256` | The amount sent to the [`universalReceiver(...)`](#universalreceiver) function. | | `typeId` **`indexed`** | `bytes32` | A `bytes32` unique identifier (= _"hook"_)that describe the type of notification, information or transaction received by the contract. Can be related to a specific standard or a hook. | -| `receivedData` | `bytes` | Any arbitrary data that was sent to the {universalReceiver(...)} function. | -| `returnedValue` | `bytes` | The value returned by the {universalReceiver(...)} function. | +| `receivedData` | `bytes` | Any arbitrary data that was sent to the [`universalReceiver(...)`](#universalreceiver) function. | +| `returnedValue` | `bytes` | The value returned by the [`universalReceiver(...)`](#universalreceiver) function. |
diff --git a/docs/contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.md b/docs/contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.md index 872dfe71f..e789fed02 100644 --- a/docs/contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.md +++ b/docs/contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.md @@ -86,13 +86,13 @@ The address of the signer will be recovered using the LSP25 signature format. #### Parameters -| Name | Type | Description | -| -------------------- | :-------: | ----------------------------------------------------------------------------------------------------------------------- | -| `signature` | `bytes` | A 65 bytes long signature generated according to the signature format specified in the LSP25 standard. | -| `nonce` | `uint256` | The nonce that the signer used to generate the `signature`. | -| `validityTimestamps` | `uint256` | The validity timestamp that the signer used to generate the signature (See {\_verifyValidityTimestamps} to learn more). | -| `msgValue` | `uint256` | The amount of native tokens intended to be sent for the relay transaction. | -| `callData` | `bytes` | The calldata to execute as a relay transaction that the signer signed for. | +| Name | Type | Description | +| -------------------- | :-------: | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `signature` | `bytes` | A 65 bytes long signature generated according to the signature format specified in the LSP25 standard. | +| `nonce` | `uint256` | The nonce that the signer used to generate the `signature`. | +| `validityTimestamps` | `uint256` | The validity timestamp that the signer used to generate the signature (See [`_verifyValidityTimestamps`](#_verifyvaliditytimestamps) to learn more). | +| `msgValue` | `uint256` | The amount of native tokens intended to be sent for the relay transaction. | +| `callData` | `bytes` | The calldata to execute as a relay transaction that the signer signed for. | #### Returns diff --git a/docs/contracts/LSP6KeyManager/LSP6KeyManager.md b/docs/contracts/LSP6KeyManager/LSP6KeyManager.md index 9812844fc..007557455 100644 --- a/docs/contracts/LSP6KeyManager/LSP6KeyManager.md +++ b/docs/contracts/LSP6KeyManager/LSP6KeyManager.md @@ -102,15 +102,15 @@ Execute A `payload` on the linked [`target`](#target) contract after having veri #### Parameters -| Name | Type | Description | -| --------- | :-----: | ---------------------------------------------------------------- | -| `payload` | `bytes` | The abi-encoded function call to execute on the linked {target}. | +| Name | Type | Description | +| --------- | :-----: | --------------------------------------------------------------------------- | +| `payload` | `bytes` | The abi-encoded function call to execute on the linked [`target`](#target). | #### Returns -| Name | Type | Description | -| ---- | :-----: | ---------------------------------------------------------------------------- | -| `0` | `bytes` | The abi-decoded data returned by the function called on the linked {target}. | +| Name | Type | Description | +| ---- | :-----: | --------------------------------------------------------------------------------------- | +| `0` | `bytes` | The abi-decoded data returned by the function called on the linked [`target`](#target). |
@@ -150,16 +150,16 @@ Same as [`execute`](#execute) but execute a batch of payloads (abi-encoded funct #### Parameters -| Name | Type | Description | -| ---------- | :---------: | -------------------------------------------------------------------------------------- | -| `values` | `uint256[]` | An array of amount of native tokens to be transferred for each `payload`. | -| `payloads` | `bytes[]` | An array of abi-encoded function calls to execute successively on the linked {target}. | +| Name | Type | Description | +| ---------- | :---------: | ------------------------------------------------------------------------------------------------- | +| `values` | `uint256[]` | An array of amount of native tokens to be transferred for each `payload`. | +| `payloads` | `bytes[]` | An array of abi-encoded function calls to execute successively on the linked [`target`](#target). | #### Returns -| Name | Type | Description | -| ---- | :-------: | ------------------------------------------------------------------------------------- | -| `0` | `bytes[]` | An array of abi-decoded data returned by the functions called on the linked {target}. | +| Name | Type | Description | +| ---- | :-------: | ------------------------------------------------------------------------------------------------ | +| `0` | `bytes[]` | An array of abi-decoded data returned by the functions called on the linked [`target`](#target). |
@@ -206,7 +206,7 @@ Allows any address (executor) to execute a payload (= abi-encoded function call) | Name | Type | Description | | -------------------- | :-------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `signature` | `bytes` | A 65 bytes long signature for a meta transaction according to LSP25. | -| `nonce` | `uint256` | The nonce of the address that signed the calldata (in a specific `_channel`), obtained via {getNonce}. Used to prevent replay attack. | +| `nonce` | `uint256` | The nonce of the address that signed the calldata (in a specific `_channel`), obtained via [`getNonce`](#getnonce). Used to prevent replay attack. | | `validityTimestamps` | `uint256` | Two `uint128` timestamps concatenated together that describes when the relay transaction is valid "from" (left `uint128`) and "until" as a deadline (right `uint128`). | | `payload` | `bytes` | The abi-encoded function call to execute. | @@ -254,13 +254,13 @@ Same as [`executeRelayCall`](#executerelaycall) but execute a batch of signed ca #### Parameters -| Name | Type | Description | -| -------------------- | :---------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `signatures` | `bytes[]` | An array of 65 bytes long signatures for meta transactions according to LSP25. | -| `nonces` | `uint256[]` | An array of nonces of the addresses that signed the calldata payloads (in specific channels). Obtained via {getNonce}. Used to prevent replay attack. | -| `validityTimestamps` | `uint256[]` | An array of two `uint128` concatenated timestamps that describe when the relay transaction is valid "from" (left `uint128`) and "until" (right `uint128`). | -| `values` | `uint256[]` | An array of amount of native tokens to be transferred for each calldata `payload`. | -| `payloads` | `bytes[]` | An array of abi-encoded function calls to be executed successively. | +| Name | Type | Description | +| -------------------- | :---------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `signatures` | `bytes[]` | An array of 65 bytes long signatures for meta transactions according to LSP25. | +| `nonces` | `uint256[]` | An array of nonces of the addresses that signed the calldata payloads (in specific channels). Obtained via [`getNonce`](#getnonce). Used to prevent replay attack. | +| `validityTimestamps` | `uint256[]` | An array of two `uint128` concatenated timestamps that describe when the relay transaction is valid "from" (left `uint128`) and "until" (right `uint128`). | +| `values` | `uint256[]` | An array of amount of native tokens to be transferred for each calldata `payload`. | +| `payloads` | `bytes[]` | An array of abi-encoded function calls to be executed successively. | #### Returns @@ -1060,13 +1060,13 @@ The address of the signer will be recovered using the LSP25 signature format. #### Parameters -| Name | Type | Description | -| -------------------- | :-------: | ----------------------------------------------------------------------------------------------------------------------- | -| `signature` | `bytes` | A 65 bytes long signature generated according to the signature format specified in the LSP25 standard. | -| `nonce` | `uint256` | The nonce that the signer used to generate the `signature`. | -| `validityTimestamps` | `uint256` | The validity timestamp that the signer used to generate the signature (See {\_verifyValidityTimestamps} to learn more). | -| `msgValue` | `uint256` | The amount of native tokens intended to be sent for the relay transaction. | -| `callData` | `bytes` | The calldata to execute as a relay transaction that the signer signed for. | +| Name | Type | Description | +| -------------------- | :-------: | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| `signature` | `bytes` | A 65 bytes long signature generated according to the signature format specified in the LSP25 standard. | +| `nonce` | `uint256` | The nonce that the signer used to generate the `signature`. | +| `validityTimestamps` | `uint256` | The validity timestamp that the signer used to generate the signature (See [`_verifyValidityTimestamps`](#_verifyvaliditytimestamps) to learn more). | +| `msgValue` | `uint256` | The amount of native tokens intended to be sent for the relay transaction. | +| `callData` | `bytes` | The calldata to execute as a relay transaction that the signer signed for. | #### Returns @@ -1180,17 +1180,17 @@ _Execute the `payload` passed to `execute(...)` or `executeRelayCall(...)`_ #### Parameters -| Name | Type | Description | -| ---------------- | :-------: | ------------------------------------------------------------------ | -| `targetContract` | `address` | - | -| `msgValue` | `uint256` | - | -| `payload` | `bytes` | The abi-encoded function call to execute on the {target} contract. | +| Name | Type | Description | +| ---------------- | :-------: | ----------------------------------------------------------------------------- | +| `targetContract` | `address` | - | +| `msgValue` | `uint256` | - | +| `payload` | `bytes` | The abi-encoded function call to execute on the [`target`](#target) contract. | #### Returns -| Name | Type | Description | -| ---- | :-----: | ------------------------------------------------------------------------- | -| `0` | `bytes` | bytes The data returned by the call made to the linked {target} contract. | +| Name | Type | Description | +| ---- | :-----: | ------------------------------------------------------------------------------------ | +| `0` | `bytes` | bytes The data returned by the call made to the linked [`target`](#target) contract. |
@@ -1209,12 +1209,12 @@ Verify if the `from` address is allowed to execute the `payload` on the [`target #### Parameters -| Name | Type | Description | -| ---------------- | :-------: | ------------------------------------------------------------------- | -| `targetContract` | `address` | The contract that is owned by the Key Manager | -| `from` | `address` | Either the caller of {execute} or the signer of {executeRelayCall}. | -| `isRelayedCall` | `bool` | - | -| `payload` | `bytes` | The abi-encoded function call to execute on the {target} contract. | +| Name | Type | Description | +| ---------------- | :-------: | ---------------------------------------------------------------------------------------------------- | +| `targetContract` | `address` | The contract that is owned by the Key Manager | +| `from` | `address` | Either the caller of [`execute`](#execute) or the signer of [`executeRelayCall`](#executerelaycall). | +| `isRelayedCall` | `bool` | - | +| `payload` | `bytes` | The abi-encoded function call to execute on the [`target`](#target) contract. |
@@ -1271,11 +1271,11 @@ Emitted when the LSP6KeyManager contract verified the permissions of the `signer #### Parameters -| Name | Type | Description | -| ------------------------ | :-------: | -------------------------------------------------------------------------------------------------------------------------------------------------- | -| `signer` **`indexed`** | `address` | The address of the controller that executed the calldata payload (either directly via {execute} or via meta transaction using {executeRelayCall}). | -| `value` **`indexed`** | `uint256` | The amount of native token to be transferred in the calldata payload. | -| `selector` **`indexed`** | `bytes4` | The bytes4 function of the function that was executed on the linked {target} | +| Name | Type | Description | +| ------------------------ | :-------: | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `signer` **`indexed`** | `address` | The address of the controller that executed the calldata payload (either directly via [`execute`](#execute) or via meta transaction using [`executeRelayCall`](#executerelaycall)). | +| `value` **`indexed`** | `uint256` | The amount of native token to be transferred in the calldata payload. | +| `selector` **`indexed`** | `bytes4` | The bytes4 function of the function that was executed on the linked [`target`](#target) |
@@ -1483,9 +1483,9 @@ Reverts when trying to call a function on the linked [`target`](#target), that i #### Parameters -| Name | Type | Description | -| ----------------- | :------: | ---------------------------------------------------------------------------------------------------------------- | -| `invalidFunction` | `bytes4` | The `bytes4` selector of the function that was attempted to be called on the linked {target} but not recognised. | +| Name | Type | Description | +| ----------------- | :------: | --------------------------------------------------------------------------------------------------------------------------- | +| `invalidFunction` | `bytes4` | The `bytes4` selector of the function that was attempted to be called on the linked [`target`](#target) but not recognised. |
@@ -1834,7 +1834,7 @@ Reverts when `from` is not authorised to call the `execute(uint256,address,uint2 | Name | Type | Description | | ---------- | :-------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `from` | `address` | The controller that tried to call the `execute(uint256,address,uint256,bytes)` function. | -| `to` | `address` | The address of an EOA or contract that `from` tried to call using the linked {target} | +| `to` | `address` | The address of an EOA or contract that `from` tried to call using the linked [`target`](#target) | | `selector` | `bytes4` | If `to` is a contract, the bytes4 selector of the function that `from` is trying to call. If no function is called (_e.g: a native token transfer_), selector = `0x00000000` |
@@ -1860,10 +1860,10 @@ Reverts when address `from` is not authorised to set the key `disallowedKey` on #### Parameters -| Name | Type | Description | -| --------------- | :-------: | ------------------------------------------------------------------------------------------------------ | -| `from` | `address` | address The controller that tried to `setData` on the linked {target}. | -| `disallowedKey` | `bytes32` | A bytes32 data key that `from` is not authorised to set on the ERC725Y storage of the linked {target}. | +| Name | Type | Description | +| --------------- | :-------: | ----------------------------------------------------------------------------------------------------------------- | +| `from` | `address` | address The controller that tried to `setData` on the linked [`target`](#target). | +| `disallowedKey` | `bytes32` | A bytes32 data key that `from` is not authorised to set on the ERC725Y storage of the linked [`target`](#target). |
diff --git a/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md b/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md index 5d22232a4..3ea9eb046 100644 --- a/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md +++ b/docs/contracts/LSP7DigitalAsset/LSP7DigitalAsset.md @@ -821,12 +821,12 @@ Mints `amount` of tokens and transfers it to `to`. #### Parameters -| Name | Type | Description | -| -------- | :-------: | ------------------------------------------------------------------------------------------------------------------------- | -| `to` | `address` | The address to mint tokens for. | -| `amount` | `uint256` | The amount of tokens to mint. | -| `force` | `bool` | A boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | -| `data` | `bytes` | Additional data the caller wants included in the emitted {Transfer} event, and sent in the LSP1 hook to the `to` address. | +| Name | Type | Description | +| -------- | :-------: | -------------------------------------------------------------------------------------------------------------------------------------- | +| `to` | `address` | The address to mint tokens for. | +| `amount` | `uint256` | The amount of tokens to mint. | +| `force` | `bool` | A boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | +| `data` | `bytes` | Additional data the caller wants included in the emitted [`Transfer`](#transfer) event, and sent in the LSP1 hook to the `to` address. |
@@ -1017,7 +1017,7 @@ If `to` is is an EOA or a contract that does not support the LSP1 interface, the | Name | Type | Description | | ---------- | :-------: | --------------------------------------------------------------------------------------------------- | -| `to` | `address` | The address to call the {universalReceiver} function on. | +| `to` | `address` | The address to call the [`universalReceiver`](#universalreceiver) function on. | | `force` | `bool` | A boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | | `lsp1Data` | `bytes` | The data to be sent to the `to` address in the `universalReceiver(...)` call. | diff --git a/docs/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md b/docs/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md index a2001b08c..5f71d36be 100644 --- a/docs/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md +++ b/docs/contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md @@ -846,12 +846,12 @@ Mints `amount` of tokens and transfers it to `to`. #### Parameters -| Name | Type | Description | -| -------- | :-------: | ------------------------------------------------------------------------------------------------------------------------- | -| `to` | `address` | The address to mint tokens for. | -| `amount` | `uint256` | The amount of tokens to mint. | -| `force` | `bool` | A boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | -| `data` | `bytes` | Additional data the caller wants included in the emitted {Transfer} event, and sent in the LSP1 hook to the `to` address. | +| Name | Type | Description | +| -------- | :-------: | -------------------------------------------------------------------------------------------------------------------------------------- | +| `to` | `address` | The address to mint tokens for. | +| `amount` | `uint256` | The amount of tokens to mint. | +| `force` | `bool` | A boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | +| `data` | `bytes` | Additional data the caller wants included in the emitted [`Transfer`](#transfer) event, and sent in the LSP1 hook to the `to` address. |
@@ -1042,7 +1042,7 @@ If `to` is is an EOA or a contract that does not support the LSP1 interface, the | Name | Type | Description | | ---------- | :-------: | --------------------------------------------------------------------------------------------------- | -| `to` | `address` | The address to call the {universalReceiver} function on. | +| `to` | `address` | The address to call the [`universalReceiver`](#universalreceiver) function on. | | `force` | `bool` | A boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | | `lsp1Data` | `bytes` | The data to be sent to the `to` address in the `universalReceiver(...)` call. | diff --git a/docs/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md b/docs/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md index 9a6ee10de..c9a8bb576 100644 --- a/docs/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md +++ b/docs/contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md @@ -1016,7 +1016,7 @@ If `to` is is an EOA or a contract that does not support the LSP1 interface, the | Name | Type | Description | | ---------- | :-------: | --------------------------------------------------------------------------------------------------- | -| `to` | `address` | The address to call the {universalReceiver} function on. | +| `to` | `address` | The address to call the [`universalReceiver`](#universalreceiver) function on. | | `force` | `bool` | A boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | | `lsp1Data` | `bytes` | The data to be sent to the `to` address in the `universalReceiver(...)` call. | diff --git a/docs/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.md b/docs/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.md index e2590db3c..4a64caca8 100644 --- a/docs/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.md +++ b/docs/contracts/LSP7DigitalAsset/extensions/LSP7CompatibleERC20.md @@ -1105,7 +1105,7 @@ If `to` is is an EOA or a contract that does not support the LSP1 interface, the | Name | Type | Description | | ---------- | :-------: | --------------------------------------------------------------------------------------------------- | -| `to` | `address` | The address to call the {universalReceiver} function on. | +| `to` | `address` | The address to call the [`universalReceiver`](#universalreceiver) function on. | | `force` | `bool` | A boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | | `lsp1Data` | `bytes` | The data to be sent to the `to` address in the `universalReceiver(...)` call. | diff --git a/docs/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.md b/docs/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.md index 5bfe41f4d..3050fa6f8 100644 --- a/docs/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.md +++ b/docs/contracts/LSP7DigitalAsset/presets/LSP7CompatibleERC20Mintable.md @@ -1139,7 +1139,7 @@ If `to` is is an EOA or a contract that does not support the LSP1 interface, the | Name | Type | Description | | ---------- | :-------: | --------------------------------------------------------------------------------------------------- | -| `to` | `address` | The address to call the {universalReceiver} function on. | +| `to` | `address` | The address to call the [`universalReceiver`](#universalreceiver) function on. | | `force` | `bool` | A boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | | `lsp1Data` | `bytes` | The data to be sent to the `to` address in the `universalReceiver(...)` call. | diff --git a/docs/contracts/LSP7DigitalAsset/presets/LSP7Mintable.md b/docs/contracts/LSP7DigitalAsset/presets/LSP7Mintable.md index a82ed62e5..15907384f 100644 --- a/docs/contracts/LSP7DigitalAsset/presets/LSP7Mintable.md +++ b/docs/contracts/LSP7DigitalAsset/presets/LSP7Mintable.md @@ -883,12 +883,12 @@ Mints `amount` of tokens and transfers it to `to`. #### Parameters -| Name | Type | Description | -| -------- | :-------: | ------------------------------------------------------------------------------------------------------------------------- | -| `to` | `address` | The address to mint tokens for. | -| `amount` | `uint256` | The amount of tokens to mint. | -| `force` | `bool` | A boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | -| `data` | `bytes` | Additional data the caller wants included in the emitted {Transfer} event, and sent in the LSP1 hook to the `to` address. | +| Name | Type | Description | +| -------- | :-------: | -------------------------------------------------------------------------------------------------------------------------------------- | +| `to` | `address` | The address to mint tokens for. | +| `amount` | `uint256` | The amount of tokens to mint. | +| `force` | `bool` | A boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | +| `data` | `bytes` | Additional data the caller wants included in the emitted [`Transfer`](#transfer) event, and sent in the LSP1 hook to the `to` address. |
@@ -1079,7 +1079,7 @@ If `to` is is an EOA or a contract that does not support the LSP1 interface, the | Name | Type | Description | | ---------- | :-------: | --------------------------------------------------------------------------------------------------- | -| `to` | `address` | The address to call the {universalReceiver} function on. | +| `to` | `address` | The address to call the [`universalReceiver`](#universalreceiver) function on. | | `force` | `bool` | A boolean that describe if transfer to a `to` address that does not support LSP1 is allowed or not. | | `lsp1Data` | `bytes` | The data to be sent to the `to` address in the `universalReceiver(...)` call. | diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md b/docs/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md index 36458f18d..77aa1e88d 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md @@ -1169,13 +1169,13 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | --------------------------------------------------------------- | -| `operator` **`indexed`** | `address` | The address revoked from the operator array ({getOperatorsOf}). | -| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | -| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `notified` | `bool` | Bool indicating whether the operator has been notified or not | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | ---------------------------------------------------------------------------------- | +| `operator` **`indexed`** | `address` | The address revoked from the operator array ([`getOperatorsOf`](#getoperatorsof)). | +| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | +| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md index c472f56dd..940acb46a 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md @@ -1195,13 +1195,13 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | --------------------------------------------------------------- | -| `operator` **`indexed`** | `address` | The address revoked from the operator array ({getOperatorsOf}). | -| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | -| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `notified` | `bool` | Bool indicating whether the operator has been notified or not | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | ---------------------------------------------------------------------------------- | +| `operator` **`indexed`** | `address` | The address revoked from the operator array ([`getOperatorsOf`](#getoperatorsof)). | +| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | +| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md index 50a1f2cb0..a67a24c75 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md @@ -1169,13 +1169,13 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | --------------------------------------------------------------- | -| `operator` **`indexed`** | `address` | The address revoked from the operator array ({getOperatorsOf}). | -| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | -| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `notified` | `bool` | Bool indicating whether the operator has been notified or not | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | ---------------------------------------------------------------------------------- | +| `operator` **`indexed`** | `address` | The address revoked from the operator array ([`getOperatorsOf`](#getoperatorsof)). | +| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | +| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.md b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.md index 080a70108..dda3eb65b 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CompatibleERC721.md @@ -1533,13 +1533,13 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | --------------------------------------------------------------- | -| `operator` **`indexed`** | `address` | The address revoked from the operator array ({getOperatorsOf}). | -| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | -| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `notified` | `bool` | Bool indicating whether the operator has been notified or not | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | ---------------------------------------------------------------------------------- | +| `operator` **`indexed`** | `address` | The address revoked from the operator array ([`getOperatorsOf`](#getoperatorsof)). | +| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | +| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md index 41cde5627..c14d1cfdf 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md @@ -1197,13 +1197,13 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | --------------------------------------------------------------- | -| `operator` **`indexed`** | `address` | The address revoked from the operator array ({getOperatorsOf}). | -| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | -| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `notified` | `bool` | Bool indicating whether the operator has been notified or not | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | ---------------------------------------------------------------------------------- | +| `operator` **`indexed`** | `address` | The address revoked from the operator array ([`getOperatorsOf`](#getoperatorsof)). | +| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | +| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.md b/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.md index 6b3867ab6..07f2900f8 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8CompatibleERC721Mintable.md @@ -1575,13 +1575,13 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | --------------------------------------------------------------- | -| `operator` **`indexed`** | `address` | The address revoked from the operator array ({getOperatorsOf}). | -| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | -| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `notified` | `bool` | Bool indicating whether the operator has been notified or not | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | ---------------------------------------------------------------------------------- | +| `operator` **`indexed`** | `address` | The address revoked from the operator array ([`getOperatorsOf`](#getoperatorsof)). | +| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | +| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.md b/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.md index a1993aa81..0610de4a7 100644 --- a/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.md +++ b/docs/contracts/LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.md @@ -1233,13 +1233,13 @@ Emitted when `tokenOwner` disables `operator` to transfer or burn `tokenId` on i #### Parameters -| Name | Type | Description | -| -------------------------- | :-------: | --------------------------------------------------------------- | -| `operator` **`indexed`** | `address` | The address revoked from the operator array ({getOperatorsOf}). | -| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | -| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | -| `notified` | `bool` | Bool indicating whether the operator has been notified or not | -| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. | +| Name | Type | Description | +| -------------------------- | :-------: | ---------------------------------------------------------------------------------- | +| `operator` **`indexed`** | `address` | The address revoked from the operator array ([`getOperatorsOf`](#getoperatorsof)). | +| `tokenOwner` **`indexed`** | `address` | The owner of the `tokenId`. | +| `tokenId` **`indexed`** | `bytes32` | The tokenId `operator` is revoked from operating on. | +| `notified` | `bool` | Bool indicating whether the operator has been notified or not | +| `operatorNotificationData` | `bytes` | The data to notify the operator about via LSP1. |
diff --git a/docs/contracts/LSP9Vault/LSP9Vault.md b/docs/contracts/LSP9Vault/LSP9Vault.md index 54603da9d..8ffdb9f81 100644 --- a/docs/contracts/LSP9Vault/LSP9Vault.md +++ b/docs/contracts/LSP9Vault/LSP9Vault.md @@ -1391,11 +1391,11 @@ Emitted when the [`universalReceiver`](#universalreceiver) function was called w | Name | Type | Description | | ---------------------- | :-------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `from` **`indexed`** | `address` | The address of the EOA or smart contract that called the {universalReceiver(...)} function. | -| `value` **`indexed`** | `uint256` | The amount sent to the {universalReceiver(...)} function. | +| `from` **`indexed`** | `address` | The address of the EOA or smart contract that called the [`universalReceiver(...)`](#universalreceiver) function. | +| `value` **`indexed`** | `uint256` | The amount sent to the [`universalReceiver(...)`](#universalreceiver) function. | | `typeId` **`indexed`** | `bytes32` | A `bytes32` unique identifier (= _"hook"_)that describe the type of notification, information or transaction received by the contract. Can be related to a specific standard or a hook. | -| `receivedData` | `bytes` | Any arbitrary data that was sent to the {universalReceiver(...)} function. | -| `returnedValue` | `bytes` | The value returned by the {universalReceiver(...)} function. | +| `receivedData` | `bytes` | Any arbitrary data that was sent to the [`universalReceiver(...)`](#universalreceiver) function. | +| `returnedValue` | `bytes` | The value returned by the [`universalReceiver(...)`](#universalreceiver) function. |
diff --git a/docs/contracts/UniversalProfile.md b/docs/contracts/UniversalProfile.md index f2003f5bc..5e34d7ab4 100644 --- a/docs/contracts/UniversalProfile.md +++ b/docs/contracts/UniversalProfile.md @@ -1415,11 +1415,11 @@ Emitted when the [`universalReceiver`](#universalreceiver) function was called w | Name | Type | Description | | ---------------------- | :-------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `from` **`indexed`** | `address` | The address of the EOA or smart contract that called the {universalReceiver(...)} function. | -| `value` **`indexed`** | `uint256` | The amount sent to the {universalReceiver(...)} function. | +| `from` **`indexed`** | `address` | The address of the EOA or smart contract that called the [`universalReceiver(...)`](#universalreceiver) function. | +| `value` **`indexed`** | `uint256` | The amount sent to the [`universalReceiver(...)`](#universalreceiver) function. | | `typeId` **`indexed`** | `bytes32` | A `bytes32` unique identifier (= _"hook"_)that describe the type of notification, information or transaction received by the contract. Can be related to a specific standard or a hook. | -| `receivedData` | `bytes` | Any arbitrary data that was sent to the {universalReceiver(...)} function. | -| `returnedValue` | `bytes` | The value returned by the {universalReceiver(...)} function. | +| `receivedData` | `bytes` | Any arbitrary data that was sent to the [`universalReceiver(...)`](#universalreceiver) function. | +| `returnedValue` | `bytes` | The value returned by the [`universalReceiver(...)`](#universalreceiver) function. |
diff --git a/dodoc/config.ts b/dodoc/config.ts index 02cb032b2..836ac0590 100644 --- a/dodoc/config.ts +++ b/dodoc/config.ts @@ -362,7 +362,7 @@ const formatParamDescription = (textToFormat: string) => { formatedText = formatedText.replace('>', '<'); } - return formatedText; + return createLocalLinks(formatedText); }; const formatParamType = (textToFormat: string) => { From 4ff7b6fe161f39d579d5a6437fa2ddc580cd7570 Mon Sep 17 00:00:00 2001 From: Andreas Richter <708186+richtera@users.noreply.github.com> Date: Mon, 6 Nov 2023 17:53:03 -0500 Subject: [PATCH 21/22] chore: Refactor verification --- constants.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/constants.ts b/constants.ts index fda71a580..6b614ea6a 100644 --- a/constants.ts +++ b/constants.ts @@ -123,8 +123,11 @@ export type LSP4DigitalAssetMetadata = { export type ImageMetadata = { width: number; height: number; - verificationFunction: string; - verificationData: string; + verification?: { + method: string; + data: string; + source?: string; + }; url: string; }; @@ -134,8 +137,11 @@ export type LinkMetadata = { }; export type AssetMetadata = { - verificationFunction: string; - verificationData: string; + verification?: { + method: string; + data: string; + source?: string; + }; url: string; fileType: string; }; From 6594f678c362bf26882023aa1a108bfd8188924d Mon Sep 17 00:00:00 2001 From: Yamen Merhi Date: Tue, 7 Nov 2023 10:14:02 +0200 Subject: [PATCH 22/22] docs: Improve natspec for LSP0 (#794) * docs: Improve natspec for LSP0 * chore: add suggested changes * docs: mark Natspec comments in `@custom` tags --------- Co-authored-by: CJ42 Co-authored-by: Jean Cvllr <31145285+CJ42@users.noreply.github.com> --- .../LSP0ERC725AccountCore.sol | 29 +++++++++-------- .../LSP0ERC725Account/LSP0ERC725Account.md | 31 ++++++++++++------- docs/contracts/UniversalProfile.md | 15 +++------ 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol index 4035228d6..97189366b 100644 --- a/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol +++ b/contracts/LSP0ERC725Account/LSP0ERC725AccountCore.sol @@ -93,7 +93,10 @@ abstract contract LSP0ERC725AccountCore is * - When receiving some native tokens without any additional data. * - On empty calls to the contract. * - * @custom:events {UniversalReceiver} event when receiving native tokens. + * @custom:info This function internally delegates the handling of native tokens to the {universalReceiver} function + * passing `_TYPEID_LSP0_VALUE_RECEIVED` as typeId and an empty bytes array as received data. + * + * @custom:events Emits a {UniversalReceiver} event when the `universalReceiver` logic is executed upon receiving native tokens. */ receive() external payable virtual { if (msg.value != 0) { @@ -120,16 +123,19 @@ abstract contract LSP0ERC725AccountCore is * * 2. If the data sent to this function is of length less than 4 bytes (not a function selector), return. * + * @custom:info Whenever the call is associated with native tokens, the function will delegate the handling of native tokens internally to the {universalReceiver} function + * passing `_TYPEID_LSP0_VALUE_RECEIVED` as typeId and the calldata as received data, except when the native token will be sent directly to the extension. + * * @custom:events {UniversalReceiver} event when receiving native tokens and extension function selector is not found or not payable. */ - // solhint-disable-next-line no-complex-fallback fallback( bytes calldata callData ) external payable virtual returns (bytes memory) { if (msg.data.length < 4) { // if value is associated with the extension call, use the universalReceiver - if (msg.value != 0) + if (msg.value != 0) { universalReceiver(_TYPEID_LSP0_VALUE_RECEIVED, callData); + } return ""; } @@ -432,6 +438,8 @@ abstract contract LSP0ERC725AccountCore is * * - If yes, call this address with the typeId and data (params), along with additional calldata consisting of 20 bytes of `msg.sender` and 32 bytes of `msg.value`. If not, continue the execution of the function. * + * This function delegates internally the handling of native tokens to the {universalReceiver} function itself passing `_TYPEID_LSP0_VALUE_RECEIVED` as typeId and the calldata as received data. + * * @param typeId The type of call received. * @param receivedData The data received. * @@ -787,14 +795,7 @@ abstract contract LSP0ERC725AccountCore is * If there is an extension for the function selector being called, it calls the extension with the * `CALL` opcode, passing the `msg.data` appended with the 20 bytes of the {msg.sender} and 32 bytes of the `msg.value`. * - * @custom:hint This function does not forward to the extension contract the `msg.value` received by the contract that inherits `LSP17Extendable`. - * If you would like to forward the `msg.value` to the extension contract, you can override the code of this internal function as follow: - * - * ```solidity - * (bool success, bytes memory result) = extension.call{value: msg.value}( - * abi.encodePacked(callData, msg.sender, msg.value) - * ); - * ``` + * @custom:hint If you would like to forward the `msg.value` to the extension contract, you should store an additional `0x01` byte after the address of the extension under the corresponding LSP17 data key. */ function _fallbackLSP17Extendable( bytes calldata callData @@ -806,8 +807,9 @@ abstract contract LSP0ERC725AccountCore is ) = _getExtensionAndForwardValue(msg.sig); // if value is associated with the extension call and extension function selector is not payable, use the universalReceiver - if (msg.value != 0 && !isForwardingValue) + if (msg.value != 0 && !isForwardingValue) { universalReceiver(_TYPEID_LSP0_VALUE_RECEIVED, callData); + } // if no extension was found for bytes4(0) return don't revert if (msg.sig == bytes4(0) && extension == address(0)) return ""; @@ -833,9 +835,10 @@ abstract contract LSP0ERC725AccountCore is } /** - * @dev Returns the extension address stored under the following data key: + * @dev Returns the extension address and the boolean indicating whether to forward the value received to the extension, stored under the following data key: * - {_LSP17_EXTENSION_PREFIX} + `` (Check [LSP2-ERC725YJSONSchema] for encoding the data key). * - If no extension is stored, returns the address(0). + * - If the stored value is 20 bytes, return false for the boolean */ function _getExtensionAndForwardValue( bytes4 functionSelector diff --git a/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md b/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md index 9a2dd2b6c..5d7b71515 100644 --- a/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md +++ b/docs/contracts/LSP0ERC725Account/LSP0ERC725Account.md @@ -86,6 +86,13 @@ Set `initialOwner` as the contract owner. ::: +:::info + +Whenever the call is associated with native tokens, the function will delegate the handling of native tokens internally to the {universalReceiver} function +passing `_TYPEID_LSP0_VALUE_RECEIVED` as typeId and the calldata as received data, except when the native token will be sent directly to the extension. + +::: + ```solidity fallback(bytes calldata callData) external payable returns (bytes memory); ``` @@ -129,6 +136,13 @@ This function is executed when: ::: +:::info + +This function internally delegates the handling of native tokens to the {universalReceiver} function +passing `_TYPEID_LSP0_VALUE_RECEIVED` as typeId and an empty bytes array as received data. + +::: + ```solidity receive() external payable; ``` @@ -143,7 +157,7 @@ Executed: **Emitted events:** -- [`UniversalReceiver`](#universalreceiver) event when receiving native tokens. +- Emits a [`UniversalReceiver`](#universalreceiver) event when the `universalReceiver` logic is executed upon receiving native tokens.
@@ -847,7 +861,7 @@ Achieves the goal of [LSP-1-UniversalReceiver] by allowing the account to be not - If there is an address stored under the data key, check if this address supports the LSP1 interfaceId. -- If yes, call this address with the typeId and data (params), along with additional calldata consisting of 20 bytes of `msg.sender` and 32 bytes of `msg.value`. If not, continue the execution of the function. +- If yes, call this address with the typeId and data (params), along with additional calldata consisting of 20 bytes of `msg.sender` and 32 bytes of `msg.value`. If not, continue the execution of the function. This function delegates internally the handling of native tokens to the [`universalReceiver`](#universalreceiver) function itself passing `_TYPEID_LSP0_VALUE_RECEIVED` as typeId and the calldata as received data.
@@ -1188,26 +1202,21 @@ function _getExtensionAndForwardValue( ) internal view returns (address, bool); ``` -Returns the extension address stored under the following data key: +Returns the extension address and the boolean indicating whether to forward the value received to the extension, stored under the following data key: - [`_LSP17_EXTENSION_PREFIX`](#_lsp17_extension_prefix) + `` (Check [LSP2-ERC725YJSONSchema] for encoding the data key). - If no extension is stored, returns the address(0). +- If the stored value is 20 bytes, return false for the boolean +
### \_fallbackLSP17Extendable :::tip Hint -This function does not forward to the extension contract the `msg.value` received by the contract that inherits `LSP17Extendable`. -If you would like to forward the `msg.value` to the extension contract, you can override the code of this internal function as follow: - -```solidity -(bool success, bytes memory result) = extension.call{value: msg.value}( - abi.encodePacked(callData, msg.sender, msg.value) -); -``` +If you would like to forward the `msg.value` to the extension contract, you should store an additional `0x01` byte after the address of the extension under the corresponding LSP17 data key. ::: diff --git a/docs/contracts/UniversalProfile.md b/docs/contracts/UniversalProfile.md index 5e34d7ab4..d2c642fce 100644 --- a/docs/contracts/UniversalProfile.md +++ b/docs/contracts/UniversalProfile.md @@ -790,7 +790,7 @@ Achieves the goal of [LSP-1-UniversalReceiver] by allowing the account to be not - If there is an address stored under the data key, check if this address supports the LSP1 interfaceId. -- If yes, call this address with the typeId and data (params), along with additional calldata consisting of 20 bytes of `msg.sender` and 32 bytes of `msg.value`. If not, continue the execution of the function. +- If yes, call this address with the typeId and data (params), along with additional calldata consisting of 20 bytes of `msg.sender` and 32 bytes of `msg.value`. If not, continue the execution of the function. This function delegates internally the handling of native tokens to the [`universalReceiver`](#universalreceiver) function itself passing `_TYPEID_LSP0_VALUE_RECEIVED` as typeId and the calldata as received data.
@@ -1131,26 +1131,21 @@ function _getExtensionAndForwardValue( ) internal view returns (address, bool); ``` -Returns the extension address stored under the following data key: +Returns the extension address and the boolean indicating whether to forward the value received to the extension, stored under the following data key: - [`_LSP17_EXTENSION_PREFIX`](#_lsp17_extension_prefix) + `` (Check [LSP2-ERC725YJSONSchema] for encoding the data key). - If no extension is stored, returns the address(0). +- If the stored value is 20 bytes, return false for the boolean +
### \_fallbackLSP17Extendable :::tip Hint -This function does not forward to the extension contract the `msg.value` received by the contract that inherits `LSP17Extendable`. -If you would like to forward the `msg.value` to the extension contract, you can override the code of this internal function as follow: - -```solidity -(bool success, bytes memory result) = extension.call{value: msg.value}( - abi.encodePacked(callData, msg.sender, msg.value) -); -``` +If you would like to forward the `msg.value` to the extension contract, you should store an additional `0x01` byte after the address of the extension under the corresponding LSP17 data key. :::