105 lines
3.3 KiB
Solidity
105 lines
3.3 KiB
Solidity
|
// SPDX-License-Identifier: CC0-1.0
|
|||
|
pragma solidity ^0.8.0;
|
|||
|
|
|||
|
import {ERC5050, Action} from "./ERC5050.sol";
|
|||
|
|
|||
|
contract Spells is ERC5050, ERC721 {
|
|||
|
|
|||
|
bytes4 constant CAST_SELECTOR = bytes4(keccak256("cast"));
|
|||
|
bytes4 constant ATTUNE_SELECTOR = bytes4(keccak256("attune"));
|
|||
|
|
|||
|
mapping(uint256 => uint256) spellDust;
|
|||
|
mapping(uint256 => string) attunement;
|
|||
|
|
|||
|
constructor() ERC721("Spells", unicode"🔮") {
|
|||
|
_registerSendable("cast");
|
|||
|
_registerReceivable("attune");
|
|||
|
}
|
|||
|
|
|||
|
function sendAction(Action memory action)
|
|||
|
external
|
|||
|
payable
|
|||
|
override
|
|||
|
onlySendableAction(action)
|
|||
|
{
|
|||
|
require(
|
|||
|
msg.sender == ownerOf(action.from._tokenId),
|
|||
|
"Spells: invalid sender"
|
|||
|
);
|
|||
|
_sendAction(action);
|
|||
|
}
|
|||
|
|
|||
|
function onActionReceived(Action calldata action, uint256 _nonce)
|
|||
|
external
|
|||
|
payable
|
|||
|
override
|
|||
|
onlyReceivableAction(action, _nonce)
|
|||
|
{
|
|||
|
if (action.selector == ATTUNE_SELECTOR) {
|
|||
|
string memory unicodeChar;
|
|||
|
bytes memory _data = action.data;
|
|||
|
assembly {
|
|||
|
// Read unicode character from first 6 bytes (\u5050)
|
|||
|
unicodeChar := shr(208, _data)
|
|||
|
}
|
|||
|
attunement[action.to._tokenId] = unicodeChar;
|
|||
|
}
|
|||
|
// Pass action to state receiver if specified
|
|||
|
_onActionReceived(action, _nonce);
|
|||
|
}
|
|||
|
|
|||
|
string[12] private dust = [
|
|||
|
unicode"․",
|
|||
|
unicode"∴",
|
|||
|
unicode"`"
|
|||
|
];
|
|||
|
|
|||
|
string[5] private spells = [
|
|||
|
"Conjuring",
|
|||
|
"Divining",
|
|||
|
"Transforming",
|
|||
|
"Hexing",
|
|||
|
"Banishing"
|
|||
|
];
|
|||
|
|
|||
|
function tokenURI(uint256 tokenId)
|
|||
|
public
|
|||
|
view
|
|||
|
override
|
|||
|
returns (string memory)
|
|||
|
{
|
|||
|
string
|
|||
|
memory out = '<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 400 350"><style>.base { fill: lightyellow; font-family: serif; font-size: 14px; } .chant { font-style: italic;} .dust {font-family: monospace; font-size: 8px; letter-spacing:5px;}.sm{font-size: 10px;} .sigil{font-family: monospace, font-size:13}</style><rect width="100%" height="100%" fill="#171717" /><text x="14" y="24" class="base">';
|
|||
|
|
|||
|
out = string.concat(
|
|||
|
out,
|
|||
|
string.concat(spells[_spellType(tokenId)], " Spell"),
|
|||
|
'</text><text x="376" y="336" class="base sigil">',
|
|||
|
attunement[tokenId]
|
|||
|
);
|
|||
|
out = string.concat(out, "</text></svg>");
|
|||
|
string memory json = Base64.encode(
|
|||
|
bytes(
|
|||
|
string(
|
|||
|
abi.encodePacked(
|
|||
|
'{"name": "Spell #',
|
|||
|
Strings.toString(tokenId),
|
|||
|
'", "description": "Cast spells, attune spells.", "image": "data:image/svg+xml;base64,',
|
|||
|
Base64.encode(bytes(out)),
|
|||
|
'"}'
|
|||
|
)
|
|||
|
)
|
|||
|
)
|
|||
|
);
|
|||
|
return string(abi.encodePacked("data:application/json;base64,", json));
|
|||
|
}
|
|||
|
|
|||
|
function _spellType(uint256 tokenId) internal pure returns (uint256) {
|
|||
|
uint256 rand = _random(Strings.toString(tokenId));
|
|||
|
return rand % 6;
|
|||
|
}
|
|||
|
|
|||
|
function _random(string memory input) internal pure returns (uint256) {
|
|||
|
return uint256(keccak256(abi.encodePacked(input)));
|
|||
|
}
|
|||
|
}
|