Accept & Reject Assets
Each user can create a Universal Receiver Delegate contract with some custom logic, which can run automatically on calls to the universalReceiver(..)
function of the user's Universal Profile based on specific typeIds.
Reject any Assets
To reject any assets received by the Universal Profile, we need to create a Universal Receiver Delegate contract that reverts when there is an asset transfer (LSP7 & LSP8). The typeId
parameter will give us more context on the call being made.
📢 Type ID to notify when receiving an LSP7 token 🪙 |
---|
TYPEID_LSP7_TOKENSRECIPIENT |
0x429ac7a06903dbc9c13dfcb3c9d11df8194581fa047c96d7a4171fc7402958ea |
📢 Type ID to notify when receiving an LSP8 NFT 🖼️ |
---|
TYPEID_LSP8_TOKENSRECIPIENT |
0x20804611b3e2ea21c480dc465142210acf4a2485947541770ec1fb87dee4a55c |
A full list of LSP1 TypeIds that can be filtered from the UniversalReceiver
event can be found under the contract > Universal Receiver TypeIds
1 - Deploy contract via Remix
- First go to the Remix's website. Create a new solidity file
UniversalReceiverDelegate.sol
under the contracts folder.
- Copy the code snippet below inside the file. It contains the logic for rejecting any LSP7 & LSP8 assets being transferred to the Universal Profile.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
// This code is only used for guides puprose, it is working but not verified nor audited.
// modules
import {LSP1UniversalReceiverDelegateUP} from "@lukso/lsp-smart-contracts/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol";
// constants
import {_TYPEID_LSP7_TOKENSRECIPIENT} from "@lukso/lsp-smart-contracts/contracts/LSP7DigitalAsset/LSP7Constants.sol";
import {_TYPEID_LSP8_TOKENSRECIPIENT} from "@lukso/lsp-smart-contracts/contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol";
contract CustomUniversalReceiverDelegate is LSP1UniversalReceiverDelegateUP {
/**
* @param asset The address of the asset being transferred to the UniversalProfile.
* @param asset The address disallowing receiving assets.
*/
error ReceivingAssetsNotAllowed(address asset, address recipient);
/**
* @dev Reverts when the typeId is relative to token receiving (LSP7 & LSP8)
* @param caller The address of the asset informing the `universalReceiver(..)` function on the UniversalProfile.
* @param value The amount of native tokens sent by the caller to the universalReceiver function on the UniversalProfile.
* @param typeId The typeId representing the context of the call to the universalReceiver function on the UniversalProfile.
* @param typeId The data sent to the universalReceiver function on the UniversalProfile.
*/
function universalReceiverDelegate(
address caller,
uint256 value,
bytes32 typeId,
bytes memory data
) public override returns (bytes memory result) {
if (typeId == _TYPEID_LSP7_TOKENSRECIPIENT || typeId == _TYPEID_LSP8_TOKENSRECIPIENT){
revert ReceivingAssetsNotAllowed(caller, msg.sender);
}
}
}
Please make sure to unlock MetaMask and disable Browser Extension while doing this step.
- Go to the Solidity Compiler tab and press the "Compile
UniversalReceiverDelegate.sol
" button. - Then navigate to the Deploy & Run Transactions tab and choose Injected Provider as the environment.
You should be connected to LUKSO Testnet in MetaMask and Remix and have enough LYXt in the EOA used to deploy the URD. If you do not have enough LYXt, request them from the LUKSO Testnet Faucet.
-
Select the
CustomUniversalReceiverDelegate
in the dropdown list of contracts and click on the deploy button. -
You will have to confirm the transaction and wait until the transaction has been validated on the network.
-
Once the contract deployed, copy and save the contract address. This address will be used in the next section.
You have successfully deployed your CustomUniversalReceiverDelegate contract on LUKSO Testnet! 🙌🏻
We now need to set its address under the LSP1-UniversalReceiverDelegate Data Key inside the UP's storage. We will do that via a custom script in step 2 using web3.js or ether.js.
2 - Install dependencies for script
Make sure you have the following dependencies installed before beginning this tutorial:
- Either
web3.js
orethers.js
@lukso/lsp-smart-contracts
- ethers
- web3
npm install ethers @lukso/lsp-smart-contracts
npm install web3 @lukso/lsp-smart-contracts
3 - Create instance of the 🆙
First we need to create an instance of the UniversalProfile
contract. We will need:
- the
UniversalProfile
ABI from the@lukso/lsp-smart-contracts
package. - the Universal Profile's address, retrieved by connecting to the UP Browser Extension
- ethers
- web3
import { ethers } from 'ethers';
import UniversalProfile from '@lukso/lsp-smart-contracts/artifacts/UniversalProfile.json';
// connect to the UP Browser Extension
const provider = new ethers.BrowserProvider(window.lukso);
// Retrieve address of the Universal Profile
const accounts = await provider.send('eth_requestAccounts', []);
// create an instance of the Universal Profile
const universalProfile = new ethers.Contract(accounts[0], UniversalProfile.abi);
import Web3 from 'web3';
import UniversalProfile from '@lukso/lsp-smart-contracts/artifacts/UniversalProfile.json';
// connect to the UP Browser Extension
const provider = new Web3(window.lukso);
// Retrieve address of the Universal Profile
const accounts = await provider.eth.requestAccounts();
// create an instance of the Universal Profile
const universalProfile = new web3.eth.Contract(
UniversalProfile.abi,
accounts[0], // Universal Profile address
);
4 - Setup the LSP1 Universal Receiver Delegate
Finally, we need to send the transaction that will update the URD of the Universal Profile.
- ethers
- web3
import { ERC725YDataKeys } from '@lukso/lsp-smart-contracts';
// code from step 2.2 ...
// Update the profile data
await universalProfile.setData(
ERC725YDataKeys.LSP1.LSP1UniversalReceiverDelegate, // URD Data Key from `@lukso/lsp-smart-contracts` package
'0x...', // address of the Universal Receiver Delegate contract deployed in step 1
{
from: accounts[0],
},
);
import { ERC725YDataKeys } from '@lukso/lsp-smart-contracts';
// code from step 2.2 ...
// Update the profile data
await universalProfile.methods
.setData(
ERC725YDataKeys.LSP1.LSP1UniversalReceiverDelegate, // URD Data Key from `@lukso/lsp-smart-contracts` package
'0x...', // address of the Universal Receiver Delegate contract deployed in step 1
)
.send({
from: accounts[0],
});
Final code
- ethers
- web3
import { ethers } from 'ethers';
import { ERC725YDataKeys } from '@lukso/lsp-smart-contracts/constants.js';
import UniversalProfile from '@lukso/lsp-smart-contracts/artifacts/UniversalProfile.json';
// connect to the UP Browser Extension
const provider = new ethers.BrowserProvider(window.lukso);
// Retrieve address of the Universal Profile
const accounts = await provider.send('eth_requestAccounts', []);
// create an instance of the Universal Profile
const universalProfile = new ethers.Contract(accounts[0], UniversalProfile.abi);
// Update the profile data
await universalProfile.setData(
ERC725YDataKeys.LSP1.LSP1UniversalReceiverDelegate, // URD Data Key from `@lukso/lsp-smart-contracts` package
'0x...', // address of the Universal Receiver Delegate contract deployed in step 1
{
from: accounts[0],
},
);
import Web3 from 'web3';
import { ERC725YDataKeys } from '@lukso/lsp-smart-contracts/constants.js';
import UniversalProfile from '@lukso/lsp-smart-contracts/artifacts/UniversalProfile.json';
// connect to the UP Browser Extension
const provider = new Web3(window.lukso);
// Retrieve address of the Universal Profile
const accounts = await provider.eth.requestAccounts();
// create an instance of the Universal Profile
const universalProfile = new web3.eth.Contract(
UniversalProfile.abi,
accounts[0], // Universal Profile address
);
// Update the profile data
await universalProfile.methods
.setData(
ERC725YDataKeys.LSP1.LSP1UniversalReceiverDelegate, // URD Data Key from `@lukso/lsp-smart-contracts` package
'0x...', // address of the Universal Receiver Delegate contract deployed in step 1
)
.send({
from: accounts[0],
});
Accepting specific Assets
To accept specific assets, you should differentiate between the different assets being transferred to you.
One way to do it is to have a mapping inside the URD contract that states if the asset being transferred is allowed to be received or not. Only the owner should be allowed to add these asset addresses. For simplicity, the owner could be the EOA address deploying the contract.
Repeat the deployment steps in Rejecting all Assets section and replace the solidity code with the one written below.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
// This code is only used for guides and demonstration purpose
// modules
import {LSP1UniversalReceiverDelegateUP} from "@lukso/lsp-smart-contracts/contracts/LSP1UniversalReceiver/LSP1UniversalReceiverDelegateUP/LSP1UniversalReceiverDelegateUP.sol";
// constants
import {_TYPEID_LSP7_TOKENSRECIPIENT} from "@lukso/lsp-smart-contracts/contracts/LSP7DigitalAsset/LSP7Constants.sol";
import {_TYPEID_LSP8_TOKENSRECIPIENT} from "@lukso/lsp-smart-contracts/contracts/LSP8IdentifiableDigitalAsset/LSP8Constants.sol";
contract CustomUniversalReceiverDelegate is LSP1UniversalReceiverDelegateUP {
address immutable public owner;
mapping (address => bool) public allowedAssets;
constructor(address _owner){
owner = _owner;
}
modifier onlyOwner(){
require(msg.sender == owner, "CustomUniversalReceiverDelegate : Caller is not the owner");
_;
}
function setAllowedAssets(address assets) public onlyOwner {
allowedAssets[assets] = true;
}
/**
* @dev Reverts when the asset being transferred is not allowed. If allowed, the address of the asset
* will be registered inside the storage, and removed when balance of the asset equal 0, according to
* the LSP5-ReceivedAssers standard.
*
* @param caller The address of the asset informing the `universalReceiver(..)` function on the UniversalProfile.
* @param value The amount of native tokens sent by the caller to the universalReceiver function on the UniversalProfile.
* @param typeId The typeId representing the context of the call to the universalReceiver function on the UniversalProfile.
* @param typeId The data sent to the universalReceiver function on the UniversalProfile.
*/
function universalReceiverDelegate(
address caller,
uint256 value,
bytes32 typeId,
bytes memory data
) public override returns (bytes memory result){
// checking if the asset being transferred is allowed or not.
if(typeId == _TYPEID_LSP8_TOKENSRECIPIENT || typeId == _TYPEID_LSP7_TOKENSRECIPIENT){
require(allowedAssets[caller], "Asset being transferred is not allowed to be received");
}
// using the default implementation code to register the address of assets received
result = super.universalReceiverDelegate(caller, value, typeId, data);
}
}
The code above will register the address of the assets allowed and remove them when the UP's balance for this asset is 0. It will also reject assets that are not allowed.
Since this code will need SUPER_SETDATA Permission, after deploying it, you will set the address of the URD in the storage using the code from the Set the address of the URD in the storage section.
A similar mapping example that list allowed assets can be found in the guide Create an LSP1 Forwarder