Skip to content

Instantly share code, notes, and snippets.

@CJ42
Last active August 17, 2023 11:06
Show Gist options
  • Save CJ42/68e263f103b8ba9dff60f3401ca28a2d to your computer and use it in GitHub Desktop.
Save CJ42/68e263f103b8ba9dff60f3401ca28a2d to your computer and use it in GitHub Desktop.
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.4;
// interfaces
import {
IERC725Y
} from "@erc725/smart-contracts/contracts/interfaces/IERC725Y.sol";
import {ILSP1UniversalReceiver} from "../ILSP1UniversalReceiver.sol";
import {ILSP7DigitalAsset} from "../../LSP7DigitalAsset/ILSP7DigitalAsset.sol";
// librairies
import {LSP2Utils} from "../../LSP2ERC725YJSONSchema/LSP2Utils.sol";
import {LSP5Utils} from "../../LSP5ReceivedAssets/LSP5Utils.sol";
import {LSP10Utils} from "../../LSP10ReceivedVaults/LSP10Utils.sol";
// errors
import {
CannotRegisterEOAsAsAssets
} from "../../LSP1UniversalReceiver//LSP1Errors.sol";
import "hardhat/console.sol";
// constants
import {
_INTERFACEID_LSP7,
_TYPEID_LSP7_TOKENSSENDER,
_TYPEID_LSP7_TOKENSRECIPIENT
} from "../../LSP7DigitalAsset/LSP7Constants.sol";
import {
_INTERFACEID_LSP8,
_TYPEID_LSP8_TOKENSSENDER,
_TYPEID_LSP8_TOKENSRECIPIENT
} from "../../LSP8IdentifiableDigitalAsset/LSP8Constants.sol";
import {
_INTERFACEID_LSP9,
_TYPEID_LSP9_OwnershipTransferred_SenderNotification,
_TYPEID_LSP9_OwnershipTransferred_RecipientNotification
} from "../../LSP9Vault/LSP9Constants.sol";
import {
_LSP5_RECEIVED_ASSETS_ARRAY_KEY,
_LSP5_RECEIVED_ASSETS_MAP_KEY_PREFIX
} from "../../LSP5ReceivedAssets/LSP5Constants.sol";
import {
_LSP10_VAULTS_ARRAY_KEY,
_LSP10_VAULTS_MAP_KEY_PREFIX
} from "../../LSP10ReceivedVaults/LSP10Constants.sol";
// refactored using function types
contract LSP1UniversalReceiverDelegateUPRefactored is ILSP1UniversalReceiver {
mapping(bytes32 => function(address) internal returns (bytes memory))
internal _typeIdToHandler;
constructor() {
// Register handlers to react to LSP7 Tokens & LSP8 NFT the were transferred
_typeIdToHandler[_TYPEID_LSP7_TOKENSSENDER] = _handleLSP7TokensSent;
_typeIdToHandler[_TYPEID_LSP8_TOKENSSENDER] = _handleLSP7TokensReceived;
// Register handlers to react to LSP7 Tokens & LSP8 NFTs that were received
_typeIdToHandler[_TYPEID_LSP7_TOKENSRECIPIENT] = _handleLSP8NFTSent;
_typeIdToHandler[_TYPEID_LSP8_TOKENSRECIPIENT] = _handleLSP8NFTReceived;
// Register handlers to react to Vaults transfers
_typeIdToHandler[
_TYPEID_LSP9_OwnershipTransferred_SenderNotification
] = _handleLSP9VaultsSent;
_typeIdToHandler[
_TYPEID_LSP9_OwnershipTransferred_RecipientNotification
] = _handleLSP9VaultsReceived;
}
function universalReceiver(
bytes32 typeId,
bytes memory /* data */
) public payable virtual returns (bytes memory result) {
address notifier = address(bytes20(msg.data[msg.data.length - 52:]));
// TODO: perform all the necessary validation in LSP1
if (notifier == tx.origin) revert CannotRegisterEOAsAsAssets(notifier);
function(address)
internal
returns (bytes memory) functionToRun = _typeIdToHandler[typeId];
bytes8 handlerFunction;
// Equality operators are not available for function types.
// We cannot do `functionToRun == bytes8(0)`, so we need to use assembly.
assembly {
handlerFunction := functionToRun
}
bool isHandlerRegistered = handlerFunction == bytes8(0);
// If it's a typeId different than LSP7/LSP8/LSP9 typeIds
if (!isHandlerRegistered) {
return "LSP1: typeId out of scope";
}
return functionToRun(notifier);
}
// TODO put these functions in a separate LSP1 extension contract and inherit from it
function _handleLSP7TokensSent(
address notifier
) internal returns (bytes memory) {
// Generate the LSP5ReceivedAssetsMap data key of the notifier
bytes32 notifierMapKey = LSP2Utils.generateMappingKey(
_LSP5_RECEIVED_ASSETS_MAP_KEY_PREFIX,
bytes20(notifier)
);
// Query the value under this data key in the ERC725Y storage
bytes memory notifierMapValue = IERC725Y(msg.sender).getData(
notifierMapKey
);
bool isMapValueSet = bytes20(notifierMapValue) != bytes20(0);
if (!isMapValueSet) return "LSP1: asset sent is not registered";
if (notifierMapValue.length < 20) return "LSP1: asset data corrupted";
uint128 arrayIndex = uint128(uint160(bytes20(notifierMapValue)));
// if the amount sent is not the full balance, then do not update the keys
uint256 balance = ILSP7DigitalAsset(notifier).balanceOf(msg.sender);
if (balance != 0) return "LSP1: full balance is not sent";
(bytes32[] memory dataKeys, bytes[] memory dataValues) = LSP5Utils
.generateSentAssetKeys(msg.sender, notifierMapKey, arrayIndex);
/**
* `generateSentAssetKeys(...)` returns empty arrays in the following cases:
* - the index returned from the data key `notifierMapKey` is bigger than
* the length of the `LSP5ReceivedAssets[]`, meaning, index is out of bounds.
*/
if (dataKeys.length == 0 && dataValues.length == 0)
return "LSP1: asset data corrupted";
// Set the LSP5 generated data keys on the account
IERC725Y(msg.sender).setDataBatch(dataKeys, dataValues);
return "";
}
function _handleLSP7TokensReceived(
address notifier
) internal returns (bytes memory) {
// Generate the LSP5ReceivedAssetsMap data key of the notifier
bytes32 notifierMapKey = LSP2Utils.generateMappingKey(
_LSP5_RECEIVED_ASSETS_MAP_KEY_PREFIX,
bytes20(notifier)
);
// Query the value under this data key in the ERC725Y storage
bytes memory notifierMapValue = IERC725Y(msg.sender).getData(
notifierMapKey
);
bool isMapValueSet = bytes20(notifierMapValue) != bytes20(0);
if (isMapValueSet) return "LSP1: asset received is already registered";
if (notifier.code.length > 0) {
// if the amount sent is 0, then do not update the keys
uint256 balance = ILSP7DigitalAsset(notifier).balanceOf(msg.sender);
if (balance == 0) return "LSP1: balance not updated";
}
(bytes32[] memory dataKeys, bytes[] memory dataValues) = LSP5Utils
.generateReceivedAssetKeys(
msg.sender,
notifier,
notifierMapKey,
_INTERFACEID_LSP7
);
// Set the LSP5 generated data keys on the account
IERC725Y(msg.sender).setDataBatch(dataKeys, dataValues);
return "";
}
function _handleLSP8NFTSent(
address notifier
) internal returns (bytes memory) {
// Generate the LSP5ReceivedAssetsMap data key of the notifier
bytes32 notifierMapKey = LSP2Utils.generateMappingKey(
_LSP5_RECEIVED_ASSETS_MAP_KEY_PREFIX,
bytes20(notifier)
);
// Query the value under this data key in the ERC725Y storage
bytes memory notifierMapValue = IERC725Y(msg.sender).getData(
notifierMapKey
);
bool isMapValueSet = bytes20(notifierMapValue) != bytes20(0);
if (!isMapValueSet) return "LSP1: asset sent is not registered";
if (notifierMapValue.length < 20) return "LSP1: asset data corrupted";
uint128 arrayIndex = uint128(uint160(bytes20(notifierMapValue)));
// if the amount sent is not the full balance, then do not update the keys
uint256 balance = ILSP7DigitalAsset(notifier).balanceOf(msg.sender);
if (balance != 0) return "LSP1: full balance is not sent";
(bytes32[] memory dataKeys, bytes[] memory dataValues) = LSP5Utils
.generateSentAssetKeys(msg.sender, notifierMapKey, arrayIndex);
/**
* `generateSentAssetKeys(...)` returns empty arrays in the following cases:
* - the index returned from the data key `notifierMapKey` is bigger than
* the length of the `LSP5ReceivedAssets[]`, meaning, index is out of bounds.
*/
if (dataKeys.length == 0 && dataValues.length == 0)
return "LSP1: asset data corrupted";
// Set the LSP5 generated data keys on the account
IERC725Y(msg.sender).setDataBatch(dataKeys, dataValues);
return "";
}
function _handleLSP8NFTReceived(
address notifier
) internal returns (bytes memory) {
// Generate the LSP5ReceivedAssetsMap data key of the notifier
bytes32 notifierMapKey = LSP2Utils.generateMappingKey(
_LSP5_RECEIVED_ASSETS_MAP_KEY_PREFIX,
bytes20(notifier)
);
// Query the value under this data key in the ERC725Y storage
bytes memory notifierMapValue = IERC725Y(msg.sender).getData(
notifierMapKey
);
bool isMapValueSet = bytes20(notifierMapValue) != bytes20(0);
if (isMapValueSet) return "LSP1: asset received is already registered";
if (notifier.code.length > 0) {
// if the amount sent is 0, then do not update the keys
uint256 balance = ILSP7DigitalAsset(notifier).balanceOf(msg.sender);
if (balance == 0) return "LSP1: balance not updated";
}
(bytes32[] memory dataKeys, bytes[] memory dataValues) = LSP5Utils
.generateReceivedAssetKeys(
msg.sender,
notifier,
notifierMapKey,
_INTERFACEID_LSP8
);
// Set the LSP5 generated data keys on the account
IERC725Y(msg.sender).setDataBatch(dataKeys, dataValues);
return "";
}
function _handleLSP9VaultsSent(
address notifier
) internal returns (bytes memory) {
// Generate the LSP5ReceivedAssetsMap data key of the notifier
bytes32 notifierMapKey = LSP2Utils.generateMappingKey(
_LSP10_VAULTS_MAP_KEY_PREFIX,
bytes20(notifier)
);
// Query the value under this data key in the ERC725Y storage
bytes memory notifierMapValue = IERC725Y(msg.sender).getData(
notifierMapKey
);
bool isMapValueSet = bytes20(notifierMapValue) != bytes20(0);
if (!isMapValueSet) return "LSP1: asset sent is not registered";
if (notifierMapValue.length < 20) return "LSP1: asset data corrupted";
uint128 arrayIndex = uint128(uint160(bytes20(notifierMapValue)));
(bytes32[] memory dataKeys, bytes[] memory dataValues) = LSP10Utils
.generateSentVaultKeys(msg.sender, notifierMapKey, arrayIndex);
/**
* `generateSentAssetKeys(...)` returns empty arrays in the following cases:
* - the index returned from the data key `notifierMapKey` is bigger than
* the length of the `LSP10Vaults[]`, meaning, index is out of bounds.
*/
if (dataKeys.length == 0 && dataValues.length == 0)
return "LSP1: asset data corrupted";
// Set the LSP10 generated data keys on the account
IERC725Y(msg.sender).setDataBatch(dataKeys, dataValues);
return "";
}
function _handleLSP9VaultsReceived(
address notifier
) internal returns (bytes memory) {
// Generate the LSP5ReceivedAssetsMap data key of the notifier
bytes32 notifierMapKey = LSP2Utils.generateMappingKey(
_LSP10_VAULTS_MAP_KEY_PREFIX,
bytes20(notifier)
);
// Query the value under this data key in the ERC725Y storage
bytes memory notifierMapValue = IERC725Y(msg.sender).getData(
notifierMapKey
);
bool isMapValueSet = bytes20(notifierMapValue) != bytes20(0);
if (isMapValueSet) return "LSP1: asset received is already registered";
(bytes32[] memory dataKeys, bytes[] memory dataValues) = LSP10Utils
.generateReceivedVaultKeys(msg.sender, notifier, notifierMapKey);
// Set the LSP10 generated data keys on the account
IERC725Y(msg.sender).setDataBatch(dataKeys, dataValues);
return "";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment