Last active
September 29, 2023 14:16
-
-
Save CJ42/d707ee4a884d69a9108e6e95d196c4bd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: Apache 2.0 | |
pragma solidity ^0.8.17; | |
import {LSP7Mintable} from "./LSP7DigitalAsset/presets/LSP7Mintable.sol"; | |
import {LSP7Burnable} from "./LSP7DigitalAsset/extensions/LSP7Burnable.sol"; | |
// for overriding internal _mint function | |
import { | |
LSP7DigitalAsset, | |
LSP7DigitalAssetCore | |
} from "./LSP7DigitalAsset/LSP7DigitalAsset.sol"; | |
contract LSP7DigitalStickers is | |
LSP7DigitalAsset, | |
// LSP7CappedSupply, // TODO: is this extension necessary if there is no way to mint after deployment? | |
LSP7Burnable | |
{ | |
constructor( | |
string memory stickerName, | |
string memory stickerSymbol, | |
address contractOwner, | |
uint256 totalNumberOfStickersInCirculation | |
) LSP7DigitalAsset(stickerName, stickerSymbol, contractOwner, true) { | |
// mint the total number of stickers that the LSP8 Collection owns | |
_mint({ | |
to: msg.sender, | |
amount: totalNumberOfStickersInCirculation, | |
force: true, | |
data: abi.encodePacked( | |
"creating ", | |
totalNumberOfStickersInCirculation, | |
" new stickers for this collection!" | |
) | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: Apache 2.0 | |
pragma solidity ^0.8.17; | |
// modules | |
import { | |
LSP8IdentifiableDigitalAsset as LSP8 | |
} from "./LSP8IdentifiableDigitalAsset/presets/LSP8Mintable.sol"; | |
import {LSP7DigitalStickers} from "./LSP7DigitalStickers.sol"; | |
// libraries | |
import { | |
Strings as StringUtils | |
} from "@openzeppelin/contracts/utils/Strings.sol"; | |
// List of LUKSO stickers collection (example): | |
// | |
// - LUKSO pink rocket --> 1_000_000 --> LSP7 contract 0x9760e8347D5E8f9042726E3B4DdE9Ae787476101 | |
// - LUKSO silver --> 850_000 --> LSP7 contract 0xF21D47961A84D00E6d8fa8430dfC456D7be75547 | |
// - LUKSO good --> 30_000 --> LSP7 contract 0x7B4d8090BB9cA1d8B081481C2a6b73b1a579C6eb | |
// - LUKSO logo --> 5_000_000 --> LSP7 contract ... | |
// - LUKSO pink pill --> 90_000 --> LSP7 contract ... | |
// - LUKSO UP blue square --> 30_000 --> LSP7 contract ... | |
// constants | |
import { | |
_LSP4_TOKEN_NAME_KEY, | |
_LSP4_METADATA_KEY | |
} from "./LSP4DigitalAssetMetadata/LSP4Constants.sol"; | |
import { | |
_LSP8_TOKENID_TYPE_ADDRESS | |
} from "./LSP8IdentifiableDigitalAsset/LSP8Constants.sol"; | |
// errors | |
import { | |
LSP8NonExistentTokenId | |
} from "./LSP8IdentifiableDigitalAsset/LSP8Errors.sol"; | |
/** | |
* @dev LSP8 collection of digital stickers, where each sticker is a LSP7 contract with a limited supply available. | |
* WARNING! | |
* This contract is for testing purpose. It has not been audited and might contain vulnerabilities. DO NOT USE IN PRODUCTION! | |
*/ | |
contract LSP8DigitalStickersCollection is | |
LSP8( | |
"Example Digital Stickers Collection", | |
"EDSC", | |
msg.sender, | |
_LSP8_TOKENID_TYPE_ADDRESS | |
) | |
{ | |
using StringUtils for string; | |
// Constants --- | |
bytes32 internal constant _LSP8_REFERENCE_CONTRACT_DATA_KEY = | |
0x708e7b881795f2e6b6c2752108c177ec89248458de3bf69d0d43480b3e5034e6; | |
bytes32 internal constant _STICKER_PRICE_DATA_KEY = | |
keccak256("Price Per Sticker"); | |
// Errors --- | |
error FailedDeployingLSP7StickerContract(string stickerName); | |
error CouldntFindStickerNameToBuyInCollection(string stickerName); | |
error InsufficientPricePaid( | |
uint256 msgValueSent, | |
uint256 paymentAmountRequired | |
); | |
error CouldntSendChangeBackToBuyer(address buyer, uint256 changeAmount); | |
function createNewStickerCollection( | |
string calldata stickerName, | |
string calldata stickerSymbol, | |
uint256 numberOfStickers, | |
uint256 pricePerSticker // in wei | |
) external onlyOwner { | |
try | |
new LSP7DigitalStickers( | |
stickerName, | |
stickerSymbol, | |
address(this), | |
numberOfStickers | |
) | |
returns (LSP7DigitalStickers newSticker) { | |
// example: | |
// newSticker (contract address)= 0x9760e8347D5E8f9042726E3B4DdE9Ae787476101 | |
// 0x0000000000000000000000009760e8347D5E8f9042726E3B4DdE9Ae787476101 | |
bytes32 stickerId = bytes32(abi.encode(address(newSticker))); | |
bytes32[] memory dataKeysToSet = new bytes32[](2); | |
bytes[] memory dataValuesToSet = new bytes[](2); | |
// Set Data Key "LSP8ReferenceContract", | |
dataKeysToSet[0] = _LSP8_REFERENCE_CONTRACT_DATA_KEY; | |
dataValuesToSet[0] = abi.encodePacked(address(this), stickerId); // "valueType": "(address,bytes32)" | |
// Set Data Key specific to the price per sticker | |
dataKeysToSet[1] = _STICKER_PRICE_DATA_KEY; | |
dataValuesToSet[1] = abi.encode(pricePerSticker); | |
newSticker.setDataBatch(dataKeysToSet, dataValuesToSet); | |
// need to pass `force = true` as will attempt to notify `universalReceiver` function of this contract | |
// which does not implement LSP1 | |
_mint({ | |
to: address(this), | |
tokenId: stickerId, | |
force: true, | |
data: "" | |
}); | |
} catch (bytes memory) { | |
revert FailedDeployingLSP7StickerContract(stickerName); | |
} | |
} | |
function setStickerMetadata( | |
bytes calldata lsp4StickerMetadataJSONURL, | |
LSP7DigitalStickers stickerContract | |
) external onlyOwner { | |
// convert the sticker contract address into a tokenId and | |
// CHECK if the sticker we are trying to buy exists | |
_existsOrError(bytes32(abi.encode(stickerContract))); | |
stickerContract.setData(_LSP4_METADATA_KEY, lsp4StickerMetadataJSONURL); | |
} | |
function getAllStickersContracts() public view returns (address[] memory) { | |
bytes32[] memory allTokenIdsFromCollections = tokenIdsOf(address(this)); | |
address[] memory stickersAddresses = new address[]( | |
allTokenIdsFromCollections.length | |
); | |
for (uint256 ii; ii < stickersAddresses.length; ++ii) { | |
address stickerAddress = address( | |
bytes20(allTokenIdsFromCollections[ii] << 96) | |
); | |
stickersAddresses[ii] = stickerAddress; | |
} | |
return stickersAddresses; | |
} | |
/** | |
* @param stickerContract (e.g: 0x9760e8347D5E8f9042726E3B4DdE9Ae787476101) | |
*/ | |
function buyStickersFromContractAddress( | |
LSP7DigitalStickers stickerContract, | |
uint256 numberOfStickersToBuy | |
) external payable { | |
// convert the sticker contract address into a tokenId and | |
// CHECK if the sticker we are trying to buy exists | |
_existsOrError(bytes32(abi.encode(stickerContract))); | |
uint256 pricePerSticker = abi.decode( | |
stickerContract.getData(_STICKER_PRICE_DATA_KEY), | |
(uint256) | |
); | |
uint256 minimumAmountToPay = numberOfStickersToBuy * pricePerSticker; | |
_checkPricePaid(minimumAmountToPay); | |
// give the change back | |
if (msg.value > minimumAmountToPay) { | |
_giveChangeBack(minimumAmountToPay); | |
} | |
stickerContract.transfer( | |
address(this), | |
msg.sender, | |
numberOfStickersToBuy, | |
false, // We require the buyer to be a UP or a contract that implements LSP1 | |
"" | |
); | |
} | |
/** | |
* | |
* @param stickerId (e.g: 0x0000000000000000000000009760e8347D5E8f9042726E3B4DdE9Ae787476101) | |
*/ | |
function buyOneStickerFromId( | |
bytes32 stickerId, | |
uint256 numberOfStickersToBuy | |
) external payable { | |
// CHECK if the sticker we are trying to buy exists | |
_existsOrError(stickerId); | |
address payable stickerContractAddress = payable( | |
address(bytes20(stickerId << 96)) | |
); | |
LSP7DigitalStickers stickerContract = LSP7DigitalStickers( | |
stickerContractAddress | |
); | |
uint256 pricePerSticker = abi.decode( | |
stickerContract.getData(_STICKER_PRICE_DATA_KEY), | |
(uint256) | |
); | |
uint256 minimumAmountToPay = numberOfStickersToBuy * pricePerSticker; | |
_checkPricePaid(minimumAmountToPay); | |
// give the change back | |
if (msg.value > minimumAmountToPay) { | |
_giveChangeBack(minimumAmountToPay); | |
} | |
// transfer the token to the buyer | |
LSP7DigitalStickers(stickerContractAddress).transfer( | |
address(this), | |
msg.sender, | |
numberOfStickersToBuy, | |
false, // We require the buyer to be a UP or a contract that implements LSP1 | |
"" | |
); | |
} | |
// Disabling this function for now as contract size is too big | |
/** | |
* @dev Requires to pay 1 LYX to buy 1 sticker :) | |
* @param stickerName e.g: "LUKSO Good" | |
*/ | |
function buyOneStickerFromName( | |
string calldata stickerName, | |
uint256 numberOfStickersToBuy | |
) external payable { | |
address[] memory allStickersContracts = getAllStickersContracts(); | |
// allocate memory arrays outside the loop to save in gas, and override the same memory address on each subsequent iteration | |
// instead of allocating new memory of each new iteration | |
bytes32[] memory dataKeysToFetch = new bytes32[](2); | |
bytes[] memory dataValuesRetrieved = new bytes[](2); | |
LSP7DigitalStickers stickerContract; | |
uint256 pricePerSticker; | |
uint256 minimumAmountToPay; | |
for (uint256 ii; ii < allStickersContracts.length; ++ii) { | |
stickerContract = LSP7DigitalStickers( | |
payable(allStickersContracts[ii]) | |
); | |
dataKeysToFetch[0] = _LSP4_TOKEN_NAME_KEY; | |
dataKeysToFetch[1] = _STICKER_PRICE_DATA_KEY; | |
dataValuesRetrieved = stickerContract.getDataBatch(dataKeysToFetch); | |
string memory lsp4TokenName = string(dataValuesRetrieved[0]); | |
if (stickerName.equal(lsp4TokenName)) { | |
pricePerSticker = abi.decode( | |
dataValuesRetrieved[1], | |
(uint256) | |
); | |
minimumAmountToPay = numberOfStickersToBuy * | |
pricePerSticker; | |
_checkPricePaid(minimumAmountToPay); | |
// give the change back | |
if (msg.value > minimumAmountToPay) { | |
_giveChangeBack(minimumAmountToPay); | |
} | |
// transfer the token to the buyer | |
stickerContract.transfer( | |
address(this), | |
msg.sender, | |
numberOfStickersToBuy, | |
false, // We require the buyer to be a UP or a contract that implements LSP1 | |
"" | |
); | |
return; | |
} | |
} | |
revert CouldntFindStickerNameToBuyInCollection(stickerName); | |
} | |
function _checkPricePaid(uint256 minimumAmountToPay) internal { | |
if (msg.value < minimumAmountToPay) { | |
revert InsufficientPricePaid(msg.value, minimumAmountToPay); | |
} else { | |
return; | |
} | |
} | |
function _giveChangeBack(uint256 minimumAmountToPay) internal { | |
(bool success, ) = msg.sender.call{ | |
value: msg.value - minimumAmountToPay | |
}(""); | |
if (!success) { | |
revert CouldntSendChangeBackToBuyer( | |
msg.sender, | |
msg.value - minimumAmountToPay | |
); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"LSP4Metadata": { | |
"description": "Silver Shiny Dog", | |
"links": [ | |
{ "title": "Twitter", "url": "https://x.com/JeanCavallera/status/1680501847482593280?s=20" }, | |
{ "title": "Website", "url": "https://lukso.network" } | |
], | |
"attributes": [ | |
{ | |
"key": "Standard type", | |
"value": "LSP", | |
"type": "string" | |
}, | |
{ | |
"key": "Standard number", | |
"value": 4, | |
"type": "number" | |
}, | |
{ | |
"key": "🆙", | |
"value": false, | |
"type": "boolean" | |
}, | |
{ | |
"key": "sticker width", | |
"value": "5cm", | |
"type": "string" | |
}, | |
{ | |
"key": "sticker height", | |
"value": "10cm", | |
"type": "string" | |
}, | |
{ | |
"key": "number of stickers in existence", | |
"value": "30,000", | |
"type": "number" | |
} | |
] | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment