Skip to content

Commit

Permalink
Merge pull request #18 from ProjectOpenSea/ryan/dynamic-traits
Browse files Browse the repository at this point in the history
Dynamic Traits: more test events, sample test metadata
  • Loading branch information
ryanio authored Nov 8, 2023
2 parents e1aaff1 + f3df598 commit e30a3a9
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 27 deletions.
123 changes: 98 additions & 25 deletions script/EmitDynamicTraitsTestEvents.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,119 @@ import {ERC721DynamicTraitsMultiUpdate} from "src/dynamic-traits/test/ERC721Dyna
import {Solarray} from "solarray/Solarray.sol";

contract EmitDynamicTraitTestEvents is Script {
ERC721DynamicTraitsMultiUpdate token;

bytes32 characterKey = keccak256("character");
bytes32 characterValue1 = bytes32("Ninja");
bytes32 characterValue2 = bytes32("Samurai");
bytes32 characterValue4 = bytes32("Sorcerer");
bytes32 characterValue5 = bytes32("Wizard");
bytes32[] characterValues = Solarray.bytes32s(characterValue1, characterValue2, characterValue4, characterValue5);

bytes32 backgroundColorKey = keccak256("backgroundColor");
bytes32 backgroundColorValue1 = bytes32(0);
bytes32 backgroundColorValue2 = bytes32(uint256(1));
bytes32 backgroundColorValue3 = bytes32(uint256(2));
bytes32 backgroundColorValue4 = bytes32(uint256(3));
bytes32 backgroundColorValue5 = bytes32(uint256(4));
bytes32[] backgroundColorValues = Solarray.bytes32s(
backgroundColorValue1,
backgroundColorValue2,
backgroundColorValue3,
backgroundColorValue4,
backgroundColorValue5
);

bytes32 nameKey = keccak256("name");
bytes32 nameValue1 = bytes32(0);
bytes32 nameValue2 = bytes32(0x92e75d5e42b80de937d204558acf69c8ea586a244fe88bc0181323fe3b9e3ebf);
bytes32 nameValue3 = bytes32("Greg");
bytes32 nameValue4 = bytes32(uint256(77));
bytes32[] nameValues = Solarray.bytes32s(nameValue1, nameValue2, nameValue3, nameValue4);

bytes32 pointsKey = keccak256("points");
bytes32 pointsValue1 = bytes32(0);
bytes32 pointsValue2 = bytes32(uint256(100));
bytes32 pointsValue3 = bytes32(uint256(201));
bytes32 pointsValue4 = bytes32(uint256(302));
bytes32[] pointsValues = Solarray.bytes32s(pointsValue1, pointsValue2, pointsValue3, pointsValue4);

bytes32 healthKey = keccak256("health");
bytes32 healthValue1 = bytes32(uint256(1000));
bytes32 healthValue2 = bytes32(uint256(943));
bytes32 healthValue3 = bytes32(uint256(471));
bytes32[] healthValues = Solarray.bytes32s(healthValue1, healthValue2, healthValue3);

bytes32 birthdayKey = keccak256("birthday");
bytes32 birthdayValue1 = bytes32(0);
bytes32 birthdayValue2 = bytes32(uint256(677919380));
bytes32 birthdayValue3 = bytes32(uint256(537670580));
bytes32 birthdayValue4 = bytes32(uint256(765417860));
bytes32[] birthdayValues = Solarray.bytes32s(birthdayValue1, birthdayValue2, birthdayValue3, birthdayValue4);

bytes32 redeemedKey = keccak256("redeemed");
bytes32 redeemedValue1 = bytes32(0);
bytes32 redeemedValue2 = bytes32(uint256(1));
bytes32[] redeemedValues = Solarray.bytes32s(redeemedValue1, redeemedValue2);

function run() public {
ERC721DynamicTraitsMultiUpdate token = new ERC721DynamicTraitsMultiUpdate();
vm.startBroadcast();

bytes32 key = bytes32("testKey");
bytes32 value = bytes32("foo");
token = new ERC721DynamicTraitsMultiUpdate();

// Emit TraitUpdated
token.mint(address(this), 0);
token.setTrait(0, key, value);
_mint(0);
token.setTrait(0, characterKey, characterValue1);

// Emit TraitUpdatedRange
uint256 fromTokenId = 1;
uint256 toTokenId = 10;
bytes32[] memory values = new bytes32[](10);
for (uint256 i = 0; i < values.length; i++) {
values[i] = bytes32(i);
}
for (uint256 tokenId = fromTokenId; tokenId <= toTokenId; tokenId++) {
token.mint(address(this), tokenId);
}
token.setTraitsRangeDifferentValues(fromTokenId, toTokenId, key, values);
_mint(fromTokenId, toTokenId);

token.setTraitsRangeDifferentValues(fromTokenId, toTokenId, characterKey, _getRange(10, characterValues));
token.setTraitsRangeDifferentValues(fromTokenId, toTokenId, backgroundColorKey, _getRange(10, characterValues));
token.setTraitsRangeDifferentValues(fromTokenId, toTokenId, nameKey, _getRange(10, nameValues));
token.setTraitsRangeDifferentValues(fromTokenId, toTokenId, pointsKey, _getRange(10, pointsValues));
token.setTraitsRangeDifferentValues(fromTokenId, toTokenId, healthKey, _getRange(10, healthValues));
token.setTraitsRangeDifferentValues(fromTokenId, toTokenId, birthdayKey, _getRange(10, birthdayValues));

// Emit TraitUpdatedRangeUniformValue
token.setTraitsRange(fromTokenId, toTokenId, key, value);
token.setTraitsRange(fromTokenId, toTokenId, healthKey, healthValue1);
token.setTraitsRange(fromTokenId, toTokenId, birthdayKey, birthdayValue1);

// Emit TraitUpdatedList
uint256[] memory tokenIds = Solarray.uint256s(100, 75, 20, 50);
values = new bytes32[](tokenIds.length);
for (uint256 i = 0; i < values.length; i++) {
values[i] = bytes32(i * 1000);
}
for (uint256 i = 0; i < tokenIds.length; i++) {
token.mint(address(this), tokenIds[i]);
}
token.setTraitsListDifferentValues(tokenIds, key, values);
uint256[] memory tokenIds = Solarray.uint256s(4, 7, 3, 9);
token.setTraitsListDifferentValues(tokenIds, pointsKey, _getRange(4, pointsValues));

// Emit TraitUpdatedListUniformValue
token.setTraitsList(tokenIds, key, value);
token.setTraitsList(tokenIds, pointsKey, pointsValue1);
token.setTraitsList(tokenIds, redeemedKey, redeemedValue1);

// Emit TraitMetadataURIUpdated
token.setTraitMetadataURI("http://example.com/1");
// file: dynamic-traits-test-metadata.json
token.setTraitMetadataURI("ipfs://QmYie5q3ARkYqs2bpjtG1RxTap4zie5rDK2WQ9feLAzpKM");
}

function _getRange(uint256 length, bytes32[] memory values) internal pure returns (bytes32[] memory result) {
result = new bytes32[](length);
for (uint256 i = 0; i < result.length; i++) {
result[i] = values[i % values.length];
}
}

function _mint(uint256 tokenId) internal {
token.mint(address(this), tokenId);
}

function _mint(uint256 fromTokenId, uint256 toTokenId) internal {
for (uint256 tokenId = fromTokenId; tokenId <= toTokenId; tokenId++) {
token.mint(address(this), tokenId);
}
}

function _mint(uint256[] memory tokenIds) internal {
for (uint256 i = 0; i < tokenIds.length; i++) {
token.mint(address(this), tokenIds[i]);
}
}
}
79 changes: 79 additions & 0 deletions script/dynamic-traits-test-metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"traits": {
"character": {
"displayName": "Character",
"dataType": {
"type": "string",
"acceptableValues": ["Ninja", "Samurai", "Sorcerer", "Wizard"],
"validateOnSale": "requireEq"
}
},
"points": {
"displayName": "Total Score",
"dataType": {
"type": "decimal",
"signed": false,
"bits": 16,
"decimals": 0
},
"validateOnSale": "requireUintGte"
},
"health": {
"displayName": "Health",
"dataType": {
"type": "decimal",
"signed": false,
"bits": 10,
"decimals": 1
},
"validateOnSale": "requireUintGte"
},
"backgroundColor": {
"displayName": "Background Color",
"dataType": {
"type": "string",
"valueMappings": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "Green",
"0x0000000000000000000000000000000000000000000000000000000000000001": "Red",
"0x0000000000000000000000000000000000000000000000000000000000000002": "Blue",
"0x0000000000000000000000000000000000000000000000000000000000000003": "Black",
"0x0000000000000000000000000000000000000000000000000000000000000004": "Orange"
}
}
},
"name": {
"displayName": "Name",
"dataType": {
"type": "string",
"minLength": 1,
"maxLength": 32,
"valueMappings": {
"0x0": "Unnamed",
"0x92e75d5e42b80de937d204558acf69c8ea586a244fe88bc0181323fe3b9e3ebf": "🙂",
"0x4772656700000000000000000000000000000000000000000000000000000000": "Greg",
"0x0000000000000000000000000000000000000000000000000000000000000077": "Johnny"
}
},
"tokenOwnerCanUpdateValue": true
},
"0x56671827fe2ad757874a68a372e21cdf22dfb12c64ee4dcef1aa9dfbfd2b8f12": {
"displayName": "Birthday 🚢 📅",
"dataType": {
"type": "epochSeconds",
"valueMappings": {
"0x0": null
}
}
},
"redeemed": {
"displayName": "Redeemed",
"dataType": {
"type": "boolean",
"valueMappings": {
"0x0": "No",
"0x0000000000000000000000000000000000000000000000000000000000000001": "Yes"
}
}
}
}
}
4 changes: 2 additions & 2 deletions src/dynamic-traits/interfaces/IERC7496.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.19;

interface IERC7496 {
/* Events */
event TraitUpdated(bytes32 indexed traitKey, uint256 tokenId, bytes32 trait);
event TraitUpdated(bytes32 indexed traitKey, uint256 tokenId, bytes32 traitValue);
event TraitUpdatedRange(bytes32 indexed traitKey, uint256 fromTokenId, uint256 toTokenId);
event TraitUpdatedRangeUniformValue(
bytes32 indexed traitKey, uint256 fromTokenId, uint256 toTokenId, bytes32 traitValue
Expand All @@ -21,7 +21,7 @@ interface IERC7496 {
function getTraitMetadataURI() external view returns (string memory uri);

/* Setters */
function setTrait(uint256 tokenId, bytes32 traitKey, bytes32 value) external;
function setTrait(uint256 tokenId, bytes32 traitKey, bytes32 traitValue) external;

/* Errors */
error TraitValueUnchanged();
Expand Down

0 comments on commit e30a3a9

Please sign in to comment.