Skip to content

Commit

Permalink
Merge pull request #662 from lukso-network/fix/lsp25-lsp6
Browse files Browse the repository at this point in the history
improve explanations of parameters for execute Relay Call guide
  • Loading branch information
CJ42 authored Nov 8, 2023
2 parents 44ac152 + 548e2d4 commit 89bd6f2
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 35 deletions.
62 changes: 62 additions & 0 deletions docs/contracts/overview/ExecuteRelayCall.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
sidebar_position: 5
---

# Execute Relay Call

## Validity timestamps

:::info

Use this website to generate validity timestamps for specific date and time.

:::

It is possible to make signatures for relay executions valid for specific time periods. This is done by constructing a **validity timestamp** that can be passed as the `uint256 validityTimestamp` parameter to `executeRelayCall` and `executeRelayCallBatch` functions.

Below are examples of constructing different **validity timestamps**:

### Example 1: from a specific date / time

Valid only from the 1st June 2024 (midnight).

Timestamp for _1st June 2024_ = `1717200000` (decimal) = `0x665A6480` (hex)

```
valid from timestamp 1717200000
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
0x000000000000000000000000665A648000000000000000000000000000000000
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
valid until indefinitely
```

### Example 2: set an expiry date

Valid until 31st January 2025, 5pm CET.

Timestamp for _31st January 2025 (5pm CET)_ = `1738339200` (decimal) = `0x679CF380` (hex)


```
valid from anytime
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
0x00000000000000000000000000000000000000000000000000000000679CF380
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
valid until 31st January 2025 (5pm CET)
```

### Example 3: valid during a specific period


Valid from 1st January 2024 (midnight CET) to 1st April 2024 (midnight CET)

- Timestamp for _1st January 2024 (midnight)_ = `1704063600` (decimal) = `0x6591F270` (hex)
- Timestamp for _1st April 2024 (midnight)_ = `1711926000` (decimal) = `0x6609EAF0` (hex)

```
valid from
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
0x0000000000000000000000006591F2700000000000000000000000006609EAF0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
valid until
```
112 changes: 83 additions & 29 deletions docs/learn/expert-guides/key-manager/execute-relay-transactions.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,11 @@ import KeyManagerContract from '@lukso/lsp-smart-contracts/artifacts/LSP6KeyMana
import { EIP191Signer } from '@lukso/eip191-signer.js';
import Web3 from 'web3';

// This is the version relative to the LSP6 standard, defined as the number 6.
import { LSP6_VERSION } from '@lukso/lsp-smart-contracts/constants';
// This is the version relative to the LSP25 standard, defined as the number 25.
import { LSP25_VERSION } from '@lukso/lsp-smart-contracts/constants';

const web3 = new Web3('https://rpc.testnet.lukso.network');
const universalProfileAddress = '0x...';
const msgValue = 0; // Amount of native tokens to be sent
const recipientAddress = '0x...';

// setup the Universal Profile controller account
Expand All @@ -95,14 +94,13 @@ import KeyManagerContract from '@lukso/lsp-smart-contracts/artifacts/LSP6KeyMana
import { EIP191Signer } from '@lukso/eip191-signer.js';
import { ethers } from 'ethers';

// This is the version relative to the LSP6 standard, defined as the number 6.
import { LSP6_VERSION } from '@lukso/lsp-smart-contracts/constants';
// This is the version relative to the LSP25 standard, defined as the number 25.
import { LSP25_VERSION } from '@lukso/lsp-smart-contracts/constants';

const provider = new ethers.providers.JsonRpcProvider(
'https://rpc.testnet.lukso.network',
);
const universalProfileAddress = '0x...';
const msgValue = 0; // Amount of native tokens to be sent
const recipientAddress = '0x...';

// setup the Universal Profile controller account
Expand Down Expand Up @@ -180,7 +178,10 @@ Encode the ABI of the transaction you want to be executed. In this case, a LYX t

:::tip

For more information about _validity timestamps_ check [**How to sign relay transactions?**](../../../standards/universal-profile/lsp6-key-manager.md#how-to-sign-relay-transactions)
The `validityTimestamp` can take different forms. For more information about _validity timestamps_, see the following pages:

- section [**Execute Relay Calls > Validity Timestamps**](../../../contracts/overview/ExecuteRelayCall.md#validity-timestamps) in our **Contracts** docs.
- [**How to sign relay transactions?**](../../../standards/universal-profile/lsp6-key-manager.md#how-to-sign-relay-transactions)

:::

Expand All @@ -195,12 +196,13 @@ const channelId = 0;
const nonce = await keyManager.methods.getNonce(controllerAccount.address, channelId).call();

const validityTimestamps = 0; // no validity timestamp set
const msgValue = 0; // Amount of native tokens to fund the UP with while calling

const abiPayload = universalProfile.methods
.execute(
0, // Operation type: CALL
recipientAddress,
msgValue,
web3.utils.toWei(3), // transfer 3 LYX to recipient
'0x', // Data
)
.encodeABI();
Expand All @@ -217,11 +219,12 @@ const channelId = 0;
const nonce = await keyManager.getNonce(controllerAccount.address, channelId);

const validityTimestamps = 0; // no validity timestamp set
const msgValue = 0; // Amount of native tokens to fund the UP with while calling

const abiPayload = universalProfile.interface.encodeFunctionData('execute', [
0, // Operation type: CALL
recipientAddress,
msgValue,
ethers.utils.parseEther('3'), // transfer 3 LYX to recipient
'0x', // Data
]);
```
Expand Down Expand Up @@ -255,13 +258,23 @@ For more information check: [**How to sign relay transactions?**](../../../stand
```typescript title="Sign the transaction"
const chainId = await web3.eth.getChainId();

// prettier-ignore
let encodedMessage = web3.utils.encodePacked(
{ value: LSP6_VERSION, type: 'uint256' },
{ value: chainId, type: 'uint256' },
{ value: nonce, type: 'uint256' },
{ value: validityTimestamps, type: 'uint256' },
{ value: msgValue, type: 'uint256' },
{ value: abiPayload, type: 'bytes' },
// MUST be number `25`
{ value: LSP25_VERSION, type: 'uint256' }, // `0x0000000000000000000000000000000000000000000000000000000000000019`
// e.g: `4201` for LUKSO Testnet
{ value: chainId, type: 'uint256' }, // `0x0000000000000000000000000000000000000000000000000000000000001069`
// e.g: nonce number 5 of the signer key X
// (the private key associated with the address of the controller that want to execute the payload)
{ value: nonce, type: 'uint256' }, // `0x0000000000000000000000000000000000000000000000000000000000000005`
// e.g: valid until 1st January 2025 at midnight (GMT).
// Timestamp = 1735689600
{ value: validityTimestamps, type: 'uint256' }, // `0x0000000000000000000000000000000000000000000000000000000067748580`
// e.g: not funding the contract with any LYX (0)
{ value: msgValue, type: 'uint256' }, // `0x0000000000000000000000000000000000000000000000000000000000000000`
// e.g: execute(uint256,address,uint256,bytes)
// send 3 LYX to address `0xcafecafecafecafeafecafecafecafeafecafecafecafeafecafecafecafe`
{ value: abiPayload, type: 'bytes' }, // `0x44c028fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cafecafecafecafecafecafecafecafecafecafe00000000000000000000000000000000000000000000000029a2241af62c000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000`
);

let eip191Signer = new EIP191Signer();
Expand All @@ -280,9 +293,25 @@ let { signature } = await eip191Signer.signDataWithIntendedValidator(
```typescript title="Sign the transaction"
const { chainId } = await provider.getNetwork();

// prettier-ignore
let encodedMessage = ethers.utils.solidityPack(
['uint256', 'uint256', 'uint256', 'uint256', 'uint256', 'bytes'],
[LSP6_VERSION, chainId, nonce, validityTimestamps, msgValue, abiPayload],
[
// MUST be number `25`
LSP25_VERSION, // `0x0000000000000000000000000000000000000000000000000000000000000019`
// e.g: `4201` for LUKSO Testnet
chainId, // `0x0000000000000000000000000000000000000000000000000000000000001069`
// e.g: nonce number 5 of the signer key X
// (the private key associated with the address of the controller that want to execute the payload)
nonce, // `0x0000000000000000000000000000000000000000000000000000000000000005`
// e.g: valid until 1st January 2025 at midnight (GMT).
// Timestamp = 1735689600
validityTimestamps, // `0x0000000000000000000000000000000000000000000000000000000067748580`
// e.g: not funding the contract with any LYX (0)
msgValue, // `0x0000000000000000000000000000000000000000000000000000000000000000`
// e.g: execute(uint256,address,uint256,bytes) -> send 3 LYX to address `0xcafecafecafecafeafecafecafecafeafecafecafecafeafecafecafecafe`
abiPayload, // `0x44c028fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cafecafecafecafecafecafecafecafecafecafe00000000000000000000000000000000000000000000000029a2241af62c000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000`
],
);

let eip191Signer = new EIP191Signer();
Expand Down Expand Up @@ -373,12 +402,11 @@ import KeyManagerContract from '@lukso/lsp-smart-contracts/artifacts/LSP6KeyMana
import { EIP191Signer } from '@lukso/eip191-signer.js';
import Web3 from 'web3';

// This is the version relative to the LSP6 standard, defined as the number 6.
import { LSP6_VERSION } from '@lukso/lsp-smart-contracts/constants';
// This is the version relative to the LSP25 standard, defined as the number 25.
import { LSP25_VERSION } from '@lukso/lsp-smart-contracts/constants';

const web3 = new Web3('https://rpc.testnet.lukso.network');
const universalProfileAddress = '0x...';
const msgValue = 0; // Amount of native tokens to be sent
const recipientAddress = '0x...';

// setup the Universal Profile controller account
Expand All @@ -402,25 +430,36 @@ const nonce = await keyManager.methods
.call();

const validityTimestamps = 0; // no validity timestamp set
const msgValue = 0; // Amount of native tokens to fund the UP with while calling

// send 3 LYX to recipient
const abiPayload = universalProfile.methods
.execute(
0, // Operation type: CALL
recipientAddress,
msgValue,
web3.utils.toWei(3), // transfer 3 LYX to recipient
'0x', // Data
)
.encodeABI();

const chainId = await web3.eth.getChainId();

// prettier-ignore
let encodedMessage = web3.utils.encodePacked(
{ value: LSP6_VERSION, type: 'uint256' },
{ value: chainId, type: 'uint256' },
{ value: nonce, type: 'uint256' },
{ value: validityTimestamps, type: 'uint256' },
{ value: msgValue, type: 'uint256' },
{ value: abiPayload, type: 'bytes' },
// MUST be number `25`
{ value: LSP25_VERSION, type: 'uint256' }, // `0x0000000000000000000000000000000000000000000000000000000000000019`
// e.g: `4201` for LUKSO Testnet
{ value: chainId, type: 'uint256' }, // `0x0000000000000000000000000000000000000000000000000000000000001069`
// e.g: nonce nb 5
{ value: nonce, type: 'uint256' }, // `0x0000000000000000000000000000000000000000000000000000000000000005`
// e.g: valid until 1st January 2025 at midnight (GMT).
// Timestamp = 1735689600
{ value: validityTimestamps, type: 'uint256' }, // `0x0000000000000000000000000000000000000000000000000000000067748580`
// e.g: not funding the contract with any LYX (0)
{ value: msgValue, type: 'uint256' }, // `0x0000000000000000000000000000000000000000000000000000000000000000`
// e.g: execute(uint256,address,uint256,bytes)
// send 3 LYX to address `0xcafecafecafecafeafecafecafecafeafecafecafecafeafecafecafecafe`
{ value: abiPayload, type: 'bytes' }, // `0x44c028fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cafecafecafecafecafecafecafecafecafecafe00000000000000000000000000000000000000000000000029a2241af62c000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000`
);

let eip191Signer = new EIP191Signer();
Expand Down Expand Up @@ -449,8 +488,8 @@ import KeyManagerContract from '@lukso/lsp-smart-contracts/artifacts/LSP6KeyMana
import { EIP191Signer } from '@lukso/eip191-signer.js';
import { ethers } from 'ethers';

// This is the version relative to the LSP6 standard, defined as the number 6.
import { LSP6_VERSION } from '@lukso/lsp-smart-contracts/constants';
// This is the version relative to the LSP25 standard, defined as the number 25.
import { LSP25_VERSION } from '@lukso/lsp-smart-contracts/constants';

const provider = new ethers.providers.JsonRpcProvider(
'https://rpc.testnet.lukso.network',
Expand Down Expand Up @@ -492,9 +531,24 @@ const abiPayload = universalProfile.interface.encodeFunctionData('execute', [

const { chainId } = await provider.getNetwork();

// prettier-ignore
let encodedMessage = ethers.utils.solidityPack(
['uint256', 'uint256', 'uint256', 'uint256', 'uint256', 'bytes'],
[LSP6_VERSION, chainId, nonce, validityTimestamps, msgValue, abiPayload],
[
// MUST be number `25`
LSP25_VERSION, // `0x0000000000000000000000000000000000000000000000000000000000000019`
// e.g: `4201` for LUKSO Testnet
chainId, // `0x0000000000000000000000000000000000000000000000000000000000001069`
// e.g: nonce nb 5
nonce, // `0x0000000000000000000000000000000000000000000000000000000000000005`
// e.g: valid until 1st January 2025 at midnight (GMT).
// Timestamp = 1735689600
validityTimestamps, // `0x0000000000000000000000000000000000000000000000000000000067748580`
// e.g: not funding the contract with any LYX (0)
msgValue, // `0x0000000000000000000000000000000000000000000000000000000000000000`
// e.g: execute(uint256,address,uint256,bytes) -> send 3 LYX to address `0xcafecafecafecafeafecafecafecafeafecafecafecafeafecafecafecafe`
abiPayload, // `0x44c028fe0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cafecafecafecafecafecafecafecafecafecafe00000000000000000000000000000000000000000000000029a2241af62c000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000`
],
);

let eip191Signer = new EIP191Signer();
Expand Down
10 changes: 5 additions & 5 deletions docs/standards/universal-profile/lsp6-key-manager.md
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,7 @@ Dapps can then leverage the relay execution features to create their own busines

![LSP6 Key Manager Relay Service](/img/standards/lsp6/lsp6-relay-execution.jpeg)

An essential aspect to consider in relay execution is the time validity of the execution signature. It's sometimes beneficial to limit the execution to be valid within a specific timeframe to prevent potential security risks. For example, if a user signs a relay transaction and the signature is stolen or compromised, the attacker could potentially use this signature indefinitely if there's no validity period set.
An essential aspect to consider in relay execution is the time validity of the execution signature. It's sometimes beneficial to limit the execution to be valid within a specific time frame to prevent potential security risks. For example, if a user signs a relay transaction and the signature is stolen or compromised, the attacker could potentially use this signature indefinitely if there's no validity period set.

To mitigate such risks, adding an optional validity timestamp to the signature could mark the start date and expiry date of its effectiveness. Once the timestamp has passed, the signature is no longer valid, rendering the relay transaction unusable.

Expand All @@ -851,8 +851,8 @@ To obtain a valid signature that can be used by anyone to execute a relayed tran
- 1. the **payload** (an abi-encoded function call) to be executed on the linked account.
- 2. the **chain id** of the blockchain where the `payload` will be executed.
- 3. the address of the [`LSP6KeyManager`](../../contracts/contracts/LSP6KeyManager/LSP6KeyManager.md) smart contract where the **payload** will be executed.
d. the Key Manager **nonce** of the controller.
- 4. the `validityTimestamps`, composed of 2 x `uint128` concatenated together, where:
- 4. the Key Manager **nonce** of the controller.
- 5. the `validityTimestamps`, composed of 2 x `uint128` concatenated together, where:

4.1. the left-side `uint128` corresponds to the timestamp from which the relay call is valid from.

Expand All @@ -869,15 +869,15 @@ To obtain a valid signature that can be used by anyone to execute a relayed tran
The relay transactions are signed using the [**version 0 of EIP191**](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-191.md#version-0x00). The relay call data that you want to sign **MUST** be the _keccak256 hash digest_ of the following elements _(bytes values)_ concatenated together.

```javascript
0x19 <0x00> <KeyManager address> <LSP6_VERSION> <chainId> <nonce> <validityTimestamps> <value> <payload>
0x19 <0x00> <KeyManager address> <LSP25_VERSION> <chainId> <nonce> <validityTimestamps> <value> <payload>
```

| Message elements | Details |
| :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `0x19` | Byte used to ensure that the _relay call signed data_ is not a valid RLP. |
| `0x00` | The [**version 0 of EIP191**](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-191.md#version-0x00). |
| `KeyManager address` | The address of the Key Manager that will execute the relay call. |
| `LSP6_VERSION` | The version of the Key Manager that will execute the relay call, as a `uint256`. (Current version of LSP6 Key Manager is **6**) |
| `LSP25_VERSION` | The `uint256` number **25** that defines the current version of the LSP25 Execute Relay Call standard. |
| `chainId` | The chain id of the blockchain where the Key Manager is deployed, as `uint256`. |
| `nonce` | The unique [**nonce**](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-6-KeyManager.md#getnonce) for the payload. |
| `validityTimestamps` | Two `uint128` timestamps concatenated, the first timestamp determines from when the payload can be executed, the second timestamp delimits the end of the validity of the payload. If `validityTimestamps` is 0, the checks of the timestamps are skipped |
Expand Down
2 changes: 1 addition & 1 deletion src/components/ContractCardsGallery/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const Contracts_AccountsInteraction = [
},
{
name: '⛽ LSP25 Execute Relay Call',
url: '',
url: './overview/ExecuteRelayCall',
urlABI: './contracts/LSP25ExecuteRelayCall/LSP25MultiChannelNonce',
description:
'Add Meta Transactions on your contract to enable gas-less transactions and more easily onboard new users.',
Expand Down

0 comments on commit 89bd6f2

Please sign in to comment.