From a8c730f608fdd585c94b69704272bdaec938a565 Mon Sep 17 00:00:00 2001 From: b00ste Date: Wed, 20 Sep 2023 14:58:02 +0300 Subject: [PATCH 1/2] feat: allow `endingTimestamp` to be 0 --- contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.sol b/contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.sol index 2d6c33598..fb8c99015 100644 --- a/contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.sol +++ b/contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce.sol @@ -113,6 +113,10 @@ abstract contract LSP25MultiChannelNonce { revert RelayCallBeforeStartTime(); } + // Allow `endingTimestamp` to be 0 + // Allow execution anytime past `startingTimestamp` + if (endingTimestamp == 0) return; + // solhint-disable-next-line not-rely-on-time if (block.timestamp > endingTimestamp) { revert RelayCallExpired(); From d6ef07ba1134329e6931bac6a6243d9b5ce8c5fb Mon Sep 17 00:00:00 2001 From: b00ste Date: Wed, 20 Sep 2023 14:58:17 +0300 Subject: [PATCH 2/2] test: add tests for new behavior --- .../Relay/ExecuteRelayCall.test.ts | 111 +++++++++++++++--- 1 file changed, 96 insertions(+), 15 deletions(-) diff --git a/tests/LSP6KeyManager/Relay/ExecuteRelayCall.test.ts b/tests/LSP6KeyManager/Relay/ExecuteRelayCall.test.ts index bf3efda48..ab6c0165e 100644 --- a/tests/LSP6KeyManager/Relay/ExecuteRelayCall.test.ts +++ b/tests/LSP6KeyManager/Relay/ExecuteRelayCall.test.ts @@ -727,11 +727,12 @@ export const shouldBehaveLikeExecuteRelayCall = ( startingTimestamp, endingTimestamp, ); + const randomNumber = 12345; const calldata = context.universalProfile.interface.encodeFunctionData('execute', [ - 0, + OPERATION_TYPES.CALL, targetContract.address, 0, - targetContract.interface.encodeFunctionData('setNumber', [nonce]), + targetContract.interface.encodeFunctionData('setNumber', [randomNumber]), ]); const value = 0; const signature = await signLSP6ExecuteRelayCall( @@ -747,7 +748,7 @@ export const shouldBehaveLikeExecuteRelayCall = ( .connect(relayer) .executeRelayCall(signature, nonce, validityTimestamps, calldata); - expect(await targetContract.getNumber()).to.equal(nonce); + expect(await targetContract.getNumber()).to.equal(randomNumber); }); }); @@ -825,11 +826,12 @@ export const shouldBehaveLikeExecuteRelayCall = ( startingTimestamp, endingTimestamp, ); + const randomNumber = 12345; const calldata = context.universalProfile.interface.encodeFunctionData('execute', [ - 0, + OPERATION_TYPES.CALL, targetContract.address, 0, - targetContract.interface.encodeFunctionData('setNumber', [nonce]), + targetContract.interface.encodeFunctionData('setNumber', [randomNumber]), ]); const value = 0; const signature = await signLSP6ExecuteRelayCall( @@ -845,7 +847,7 @@ export const shouldBehaveLikeExecuteRelayCall = ( .connect(relayer) .executeRelayCall(signature, nonce, validityTimestamps, calldata); - expect(await targetContract.getNumber()).to.equal(nonce); + expect(await targetContract.getNumber()).to.equal(randomNumber); }); }); @@ -862,11 +864,12 @@ export const shouldBehaveLikeExecuteRelayCall = ( startingTimestamp, endingTimestamp, ); + const randomNumber = 12345; const calldata = context.universalProfile.interface.encodeFunctionData('execute', [ - 0, + OPERATION_TYPES.CALL, targetContract.address, 0, - targetContract.interface.encodeFunctionData('setNumber', [nonce]), + targetContract.interface.encodeFunctionData('setNumber', [randomNumber]), ]); const value = 0; const signature = await signLSP6ExecuteRelayCall( @@ -884,7 +887,7 @@ export const shouldBehaveLikeExecuteRelayCall = ( .connect(relayer) .executeRelayCall(signature, nonce, validityTimestamps, calldata); - expect(await targetContract.getNumber()).to.equal(nonce); + expect(await targetContract.getNumber()).to.equal(randomNumber); }); }); }); @@ -964,11 +967,12 @@ export const shouldBehaveLikeExecuteRelayCall = ( startingTimestamp, endingTimestamp, ); + const randomNumber = 12345; const calldata = context.universalProfile.interface.encodeFunctionData('execute', [ - 0, + OPERATION_TYPES.CALL, targetContract.address, 0, - targetContract.interface.encodeFunctionData('setNumber', [nonce]), + targetContract.interface.encodeFunctionData('setNumber', [randomNumber]), ]); const value = 0; const signature = await signLSP6ExecuteRelayCall( @@ -986,7 +990,7 @@ export const shouldBehaveLikeExecuteRelayCall = ( .connect(relayer) .executeRelayCall(signature, nonce, validityTimestamps, calldata); - expect(await targetContract.getNumber()).to.equal(nonce); + expect(await targetContract.getNumber()).to.equal(randomNumber); }); }); }); @@ -995,11 +999,12 @@ export const shouldBehaveLikeExecuteRelayCall = ( it('passes', async () => { const nonce = await context.keyManager.callStatic.getNonce(signer.address, 14); const validityTimestamps = 0; + const randomNumber = 12345; const calldata = context.universalProfile.interface.encodeFunctionData('execute', [ - 0, + OPERATION_TYPES.CALL, targetContract.address, 0, - targetContract.interface.encodeFunctionData('setNumber', [nonce]), + targetContract.interface.encodeFunctionData('setNumber', [randomNumber]), ]); const value = 0; const signature = await signLSP6ExecuteRelayCall( @@ -1015,7 +1020,83 @@ export const shouldBehaveLikeExecuteRelayCall = ( .connect(relayer) .executeRelayCall(signature, nonce, validityTimestamps, calldata); - expect(await targetContract.getNumber()).to.equal(nonce); + expect(await targetContract.getNumber()).to.equal(randomNumber); + }); + }); + + describe('when `endingTimestamp == 0`', () => { + describe('`startingTimestamp` < now', () => { + it('passes', async () => { + const now = await time.latest(); + + const startingTimestamp = now - 100; + const endingTimestamp = 0; + + const nonce = await context.keyManager.callStatic.getNonce(signer.address, 14); + const validityTimestamps = createValidityTimestamps( + startingTimestamp, + endingTimestamp, + ); + const randomNumber = 12345; + const calldata = context.universalProfile.interface.encodeFunctionData('execute', [ + OPERATION_TYPES.CALL, + targetContract.address, + 0, + targetContract.interface.encodeFunctionData('setNumber', [randomNumber]), + ]); + const value = 0; + const signature = await signLSP6ExecuteRelayCall( + context.keyManager, + nonce.toString(), + validityTimestamps, + signerPrivateKey, + value, + calldata, + ); + + await context.keyManager + .connect(relayer) + .executeRelayCall(signature, nonce, validityTimestamps, calldata); + + expect(await targetContract.getNumber()).to.equal(randomNumber); + }); + }); + + describe('`startingTimestamp` > now', () => { + it('reverts', async () => { + const now = await time.latest(); + + const startingTimestamp = now + 100; + const endingTimestamp = 0; + + const nonce = await context.keyManager.callStatic.getNonce(signer.address, 14); + const validityTimestamps = createValidityTimestamps( + startingTimestamp, + endingTimestamp, + ); + const randomNumber = 12345; + const calldata = context.universalProfile.interface.encodeFunctionData('execute', [ + OPERATION_TYPES.CALL, + targetContract.address, + 0, + targetContract.interface.encodeFunctionData('setNumber', [randomNumber]), + ]); + const value = 0; + const signature = await signLSP6ExecuteRelayCall( + context.keyManager, + nonce.toString(), + validityTimestamps, + signerPrivateKey, + value, + calldata, + ); + + await expect( + context.keyManager + .connect(relayer) + .executeRelayCall(signature, nonce, validityTimestamps, calldata), + ).to.be.revertedWithCustomError(context.keyManager, 'RelayCallBeforeStartTime'); + }); }); }); });