Skip to main content

Implement LSP1

Disclaimer

This guide might contain outdated information and will be updated soon.

note

Users deploying their Universal Profiles using the guides that utilize lsp-factory or the Browser Extension can skip this guide, as this contract is already deployed and set for their profiles.

This guide will teach you how to deploy and set the default implementation of the Universal Receiver Delegate (URD) used by the Universal Profile. This contract will register the addresses of the received assets and vaults and will remove them on a balance equal to 0. This contract requires the SUPER_SETDATA Permission to interact with the profile through the KeyManager.

UniversalReceiverDelegate setting data keys on profile

Setup​

Make sure you have the following dependencies installed before beginning this tutorial:

Install the dependencies
npm install web3 @lukso/lsp-smart-contracts @erc725/erc725.js

Step 1 - Imports, constants and EOA​

For beginners we need to get the ABIs of the contracts that we will use and the bytecode of the LSP1UniversalReceiverDelegateUP.
After that we need to store the address of our Universal Profile.
Then we will initialize the EOA that we will further use.

Imports, Constants & EOA
import LSP1UniversalReceiverDelegateUP from '@lukso/lsp-smart-contracts/artifacts/LSP1UniversalReceiverDelegateUP.json';
import UniversalProfile from '@lukso/lsp-smart-contracts/artifacts/UniversalProfile.json';
import {
ERC725YDataKeys,
PERMISSIONS,
} from '@lukso/lsp-smart-contracts/constants.js';
import Web3 from 'web3';

// constants
const web3 = new Web3('https://rpc.testnet.lukso.network');
const URD_DATA_KEY = ERC725YDataKeys.LSP1.LSP1UniversalReceiverDelegate;
const universalProfileAddress = '0x...';

// setup your EOA
const privateKey = '0x...';
const EOA = web3.eth.accounts.wallet.add(privateKey);

Step 2 - Deploy the default Universal Receiver Delegate contract​

info

The Universal Profile and the Vault don't use the same implementation of the Universal Receiver Delegate.

Create a contract instance​

At this step we will create an instance of the Universal profile URD that we will further be used to deploy one.

Contract instance of the Universal Profile URD
// create an instance of the LSP1UniversalReceiverDelegateUP
let universalProfileURD = new web3.eth.Contract(
LSP1UniversalReceiverDelegateUP.abi,
);

Send the contract deployment transaction​

Send the deployment transaction to get a newly deployed URD.

Send the transaction for deploying a new Universal Profile URD
// deploy the Universal Receiver Delegate UP contract
await universalProfileURD
.deploy({
data: LSP1UniversalReceiverDelegateUP.bytecode,
})
.send({
from: EOA.address,
gas: 5_000_000,
gasPrice: '1000000000',
});

Final code​

Deploy a Universal Receiver Delegate for the Universal Profile
const deployUniversalProfileURD = async () => {
// create an instance of the LSP1UniversalReceiverDelegateUP
const universalProfileURD = new web3.eth.Contract(
LSP1UniversalReceiverDelegateUP.abi,
);
let universalProfileURDAddress;

// deploy the Universal Receiver Delegate UP contract
await universalProfileURD
.deploy({
data: LSP1UniversalReceiverDelegateUP.bytecode,
})
.send({
from: EOA.address,
gas: 5_000_000,
gasPrice: '1000000000',
})
.on('receipt', (receipt) => {
universalProfileURDAddress = receipt.contractAddress;
});

return universalProfileURDAddress;
};

// deploy a new Universal Profile URD and retrieve its address
const universalProfileURDAddress = await deployUniversalProfileURD();

Step 3 - Set the address of the URD in the storage​

After deploying the contract, we need to set its address under the LSP1-UniversalReceiverDelegate Data Key and grant it the SUPER_SETDATA permission.

Create an UP contract instance​

Firstly we need to create an instance for the Universal Profile contract.

Contract instance for the Universal Profile
// create an instance of the Universal Profile
const universalProfile = new web3.eth.Contract(
UniversalProfile.abi,
universalProfileAddress,
);

Register URD on the UP + set the URD permissions​

Generate Data Keys & Values for adding a URD to the Universal Profile and for granting SUPER_SETDATA permission to the URD.

Encode Data Keys & Values for updating the URD and its permissions
import { ERC725 } from '@erc725/erc725.js';

const addressPermissionsOldArrayLengthHex = await universalProfile.methods[
'getData(bytes32)'
](ERC725YDataKeys.LSP6['AddressPermissions[]'].length).call();

const addressPermissionsNewArrayLength =
web3.utils.hexToNumber(addressPermissionsOldArrayLengthHex) + 1;

const addressPermissionsNewArrayLengthHex = web3.utils.padLeft(
web3.utils.numberToHex(addressPermissionsNewArrayLength),
32,
);

// create bytes32 permission value for the LSP1 Delegate
const lsp1DelegatePermissions = ERC725.encodePermissions({
SUPER_SETDATA: true,
REENTRANCY: true,
});

// bytes16 index `addressPermissionsOldArrayLengthHex` will serve as index
const newElementIndexInArrayHex =
addressPermissionsOldArrayLengthHex.substring(2);

const dataKeys = [
URD_DATA_KEY,
ERC725YDataKeys.LSP6['AddressPermissions[]'].length,
ERC725YDataKeys.LSP6['AddressPermissions[]'].index +
newElementIndexInArrayHex,
ERC725YDataKeys.LSP6['AddressPermissions:Permissions'] +
universalProfileURDAddress.substring(2),
];
const dataValues = [
universalProfileURDAddress,
addressPermissionsNewArrayLengthHex,
universalProfileURDAddress,
lsp1DelegatePermissions,
];

Set the URD and its permissions​

Lastly, we need to send the transaction that will update the URD and its permissions on the Universal Profile.

Update the data on the Universal Profile
// update the Universal Profile data
await universalProfile.methods.setDataBatch(dataKeys, dataValues).send({
from: EOA.address,
gasLimit: 600_000,
});

Final code​

Update the URD of the Universal Profile and its permissions
import { ERC725 } from '@erc725/erc725.js';

const updateUniversalProfileURD = async (universalProfileURDAddress) => {
// create an instance of the Universal Profile
const universalProfile = new web3.eth.Contract(
UniversalProfile.abi,
universalProfileAddress,
);

const addressPermissionsOldArrayLengthHex = await universalProfile.methods[
'getData(bytes32)'
](ERC725YDataKeys.LSP6['AddressPermissions[]'].length).call();

const addressPermissionsNewArrayLength =
web3.utils.hexToNumber(addressPermissionsOldArrayLengthHex) + 1;

const addressPermissionsNewArrayLengthHex = web3.utils.padLeft(
web3.utils.numberToHex(addressPermissionsNewArrayLength),
32,
);

// create bytes32 permission value for the LSP1 Delegate
const lsp1DelegatePermissions = ERC725.encodePermissions({
SUPER_SETDATA: true,
REENTRANCY: true,
});

// bytes16 index `addressPermissionsOldArrayLengthHex` will serve as index
const newElementIndexInArrayHex =
addressPermissionsOldArrayLengthHex.substring(2);

const dataKeys = [
URD_DATA_KEY,
ERC725YDataKeys.LSP6['AddressPermissions[]'].length,
ERC725YDataKeys.LSP6['AddressPermissions[]'].index +
newElementIndexInArrayHex,
ERC725YDataKeys.LSP6['AddressPermissions:Permissions'] +
universalProfileURDAddress.substring(2),
];
const dataValues = [
universalProfileURDAddress,
addressPermissionsNewArrayLengthHex,
universalProfileURDAddress,
lsp1DelegatePermissions,
];

// update the Universal Profile data
await universalProfile.methods.setDataBatch(dataKeys, dataValues).send({
from: EOA.address,
gasLimit: 600_000,
});
};

// update the URD of the Universal profile
await updateUniversalProfileURD(universalProfileURDAddress);

Final code - Deploy & Update​

Deploy a Universal Profile URD, update its permissions and add it to the Universal Profile
import LSP1UniversalReceiverDelegateUP from '@lukso/lsp-smart-contracts/artifacts/LSP1UniversalReceiverDelegateUP.json';
import UniversalProfile from '@lukso/lsp-smart-contracts/artifacts/UniversalProfile.json';
import {
ERC725YDataKeys,
PERMISSIONS,
} from '@lukso/lsp-smart-contracts/constants.js';
import Web3 from 'web3';
import { ERC725 } from '@erc725/erc725.js';

// constants
const web3 = new Web3('https://rpc.testnet.lukso.network');
const URD_DATA_KEY = ERC725YDataKeys.LSP1.LSP1UniversalReceiverDelegate;
const universalProfileAddress = '0x...';

// setup your EOA
const privateKey = '0x...';
const EOA = web3.eth.accounts.wallet.add(privateKey);

const deployUniversalProfileURD = async () => {
// create an instance of the LSP1UniversalReceiverDelegateUP
const universalProfileURD = new web3.eth.Contract(
LSP1UniversalReceiverDelegateUP.abi,
);
let universalProfileURDAddress;

// deploy the Universal Receiver Delegate UP contract
await universalProfileURD
.deploy({
data: LSP1UniversalReceiverDelegateUP.bytecode,
})
.send({
from: EOA.address,
gas: 5_000_000,
gasPrice: '1000000000',
})
.on('receipt', (receipt) => {
universalProfileURDAddress = receipt.contractAddress;
});

return universalProfileURDAddress;
};

const updateUniversalProfileURD = async (universalProfileURDAddress) => {
// create an instance of the Universal Profile
const universalProfile = new web3.eth.Contract(
UniversalProfile.abi,
universalProfileAddress,
);

const addressPermissionsOldArrayLengthHex = await universalProfile.methods[
'getData(bytes32)'
](ERC725YDataKeys.LSP6['AddressPermissions[]'].length).call();

const addressPermissionsNewArrayLength =
web3.utils.hexToNumber(addressPermissionsOldArrayLengthHex) + 1;

const addressPermissionsNewArrayLengthHex = web3.utils.padLeft(
web3.utils.numberToHex(addressPermissionsNewArrayLength),
32,
);

// create bytes32 permission value for the LSP1 Delegate
const lsp1DelegatePermissions = ERC725.encodePermissions({
SUPER_SETDATA: true,
REENTRANCY: true,
});

// bytes16 index `addressPermissionsOldArrayLengthHex` will serve as index
const newElementIndexInArrayHex =
addressPermissionsOldArrayLengthHex.substring(2);

const dataKeys = [
URD_DATA_KEY,
ERC725YDataKeys.LSP6['AddressPermissions[]'].length,
ERC725YDataKeys.LSP6['AddressPermissions[]'].index +
newElementIndexInArrayHex,
ERC725YDataKeys.LSP6['AddressPermissions:Permissions'] +
universalProfileURDAddress.substring(2),
];
const dataValues = [
universalProfileURDAddress,
addressPermissionsNewArrayLengthHex,
universalProfileURDAddress,
lsp1DelegatePermissions,
];

// update the Universal Profile data
await universalProfile.methods.setDataBatch(dataKeys, dataValues).send({
from: EOA.address,
gasLimit: 600_000,
});
};

// deploy a new Universal Profile URD and retrieve its address
const universalProfileURDAddress = await deployUniversalProfileURD();

// update the URD of the Universal profile
await updateUniversalProfileURD(universalProfileURDAddress);