Skip to content

Commit

Permalink
rename and add logic
Browse files Browse the repository at this point in the history
  • Loading branch information
andresaiello committed May 24, 2024
1 parent b6cf391 commit 5108de2
Show file tree
Hide file tree
Showing 3 changed files with 262 additions and 135 deletions.
138 changes: 70 additions & 68 deletions packages/zevm-app-contracts/contracts/xp-nft/xpNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ contract ZetaXP is ERC721URIStorage, Ownable {
uint256 count;
}

struct TokenData {
struct ZetaXPData {
uint256 xpTotal;
uint256 level;
uint256 testnetCampaignParticipant;
Expand All @@ -26,8 +26,19 @@ contract ZetaXP is ERC721URIStorage, Ownable {
uint256 generation;
}

mapping(uint256 => TokenData) public tokenData;
struct UpdateData {
address to;
uint256 tokenId;
ZetaXPData xpData;
uint256[] taskIds;
Task[] taskValues;
Signature signature;
uint256 sigTimestamp;
}

mapping(uint256 => ZetaXPData) public zetaXPData;
mapping(uint256 => mapping(uint256 => Task)) public tasksByTokenId;
mapping(uint256 => uint256) lastUpdateTimestampByTokenId;

// Base URL for NFT images
string public baseTokenURI;
Expand All @@ -40,6 +51,8 @@ contract ZetaXP is ERC721URIStorage, Ownable {

error InvalidSigner();
error LengthMismatch();
error TransferNotAllowed();
error OutdatedSignature();

constructor(
string memory name,
Expand Down Expand Up @@ -83,98 +96,87 @@ contract ZetaXP is ERC721URIStorage, Ownable {
return string(bstr);
}

function _verify(
address to,
uint256 tokenId,
TokenData memory data_,
uint256[] calldata taskIds,
Task[] calldata taskValues,
Signature calldata signature
) private view {
bytes32 payloadHash = _calculateHash(to, tokenId, data_, taskIds, taskValues);
function _verify(UpdateData memory updateData) private view {
bytes32 payloadHash = _calculateHash(updateData);
bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash));

address messageSigner = ecrecover(messageHash, signature.v, signature.r, signature.s);
address messageSigner = ecrecover(
messageHash,
updateData.signature.v,
updateData.signature.r,
updateData.signature.s
);

if (signerAddress != messageSigner) revert InvalidSigner();
if (updateData.sigTimestamp <= lastUpdateTimestampByTokenId[updateData.tokenId]) revert OutdatedSignature();
}

// Function to compute the hash of the data and tasks for a token
function _calculateHash(
address to,
uint256 tokenId,
TokenData memory data_,
uint256[] memory taskIds,
Task[] memory taskValues
) private pure returns (bytes32) {
function _calculateHash(UpdateData memory updateData) private pure returns (bytes32) {
ZetaXPData memory xpData = updateData.xpData;
bytes memory encodedData = abi.encode(
to,
tokenId,
data_.xpTotal,
data_.level,
data_.testnetCampaignParticipant,
data_.enrollDate,
data_.mintDate,
data_.generation
updateData.to,
updateData.tokenId,
updateData.sigTimestamp,
xpData.xpTotal,
xpData.level,
xpData.testnetCampaignParticipant,
xpData.enrollDate,
xpData.mintDate,
xpData.generation
);

for (uint256 i = 0; i < taskIds.length; i++) {
encodedData = abi.encode(encodedData, taskIds[i], taskValues[i].completed, taskValues[i].count);
for (uint256 i = 0; i < updateData.taskIds.length; i++) {
encodedData = abi.encode(
encodedData,
updateData.taskIds[i],
updateData.taskValues[i].completed,
updateData.taskValues[i].count
);
}

return keccak256(encodedData);
}

function _updateNFT(
address to,
uint256 tokenId,
TokenData memory data_,
uint256[] calldata taskIds,
Task[] calldata taskValues,
Signature calldata signature
) internal {
_verify(to, tokenId, data_, taskIds, taskValues, signature);
if (taskIds.length != taskValues.length) revert LengthMismatch();

tokenData[tokenId] = data_;
for (uint256 i = 0; i < taskIds.length; i++) {
tasksByTokenId[tokenId][taskIds[i]] = taskValues[i];
function _updateNFT(UpdateData memory updateData) internal {
_verify(updateData);
lastUpdateTimestampByTokenId[updateData.tokenId] = updateData.sigTimestamp;
ZetaXPData memory xpData = updateData.xpData;
zetaXPData[updateData.tokenId] = xpData;

if (updateData.taskIds.length != updateData.taskValues.length) revert LengthMismatch();

zetaXPData[updateData.tokenId] = updateData.xpData;
for (uint256 i = 0; i < updateData.taskIds.length; i++) {
tasksByTokenId[updateData.tokenId][updateData.taskIds[i]] = updateData.taskValues[i];
}
}

// External mint function
function mintNFT(
address to,
uint256 tokenId,
TokenData memory data_,
uint256[] calldata taskIds,
Task[] calldata taskValues,
Signature calldata signature
) external {
_mint(to, tokenId);
_setTokenURI(tokenId, string(abi.encodePacked(baseTokenURI, _uint2str(tokenId))));

_updateNFT(to, tokenId, data_, taskIds, taskValues, signature);

emit NewNFTMinted(to, tokenId);
function mintNFT(UpdateData calldata mintData) external {
_mint(mintData.to, mintData.tokenId);
_setTokenURI(mintData.tokenId, string(abi.encodePacked(baseTokenURI, _uint2str(mintData.tokenId))));

_updateNFT(mintData);

emit NewNFTMinted(mintData.to, mintData.tokenId);
}

// External mint function
function updateNFT(
uint256 tokenId,
TokenData memory data_,
uint256[] calldata taskIds,
Task[] calldata taskValues,
Signature calldata signature
) external {
address owner = ownerOf(tokenId);
_updateNFT(owner, tokenId, data_, taskIds, taskValues, signature);

emit NFTUpdated(owner, tokenId);
function updateNFT(UpdateData memory updateData) external {
address owner = ownerOf(updateData.tokenId);
updateData.to = owner;
_updateNFT(updateData);

emit NFTUpdated(owner, updateData.tokenId);
}

// Set the base URI for tokens
function setBaseURI(string calldata _uri) external onlyOwner {
baseTokenURI = _uri;
}

function _transfer(address from, address to, uint256 tokenId) internal override {
revert TransferNotAllowed();
}
}
44 changes: 28 additions & 16 deletions packages/zevm-app-contracts/test/xp-nft/test.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export interface Task {
count: number;
}

export interface TokenData {
export interface ZetaXPData {
enrollDate: number;
generation: number;
level: number;
Expand All @@ -15,40 +15,52 @@ export interface TokenData {
xpTotal: number;
}

export interface Signature {
r: string;
s: string;
v: number;
}

export interface NFT {
data: TokenData;
tasks: Task[];
tasksId: number[];
taskIds: number[];
taskValues: Task[];
to: string;
tokenId: number;
xpData: ZetaXPData;
}

export interface UpdateParam extends NFT {
sigTimestamp: number;
signature: Signature;
}

export const getSignature = async (
signer: SignerWithAddress,
timestamp: number,
to: string,
tokenId: number,
data: TokenData,
tasksId: number[],
tasks: Task[]
nft: NFT
) => {
const { xpData } = nft;
let payload = ethers.utils.defaultAbiCoder.encode(
["address", "uint256", "uint256", "uint256", "uint256", "uint256", "uint256", "uint256"],
["address", "uint256", "uint256", "uint256", "uint256", "uint256", "uint256", "uint256", "uint256"],
[
to,
tokenId,
data.xpTotal,
data.level,
data.testnetCampaignParticipant,
data.enrollDate,
data.mintDate,
data.generation,
timestamp,
xpData.xpTotal,
xpData.level,
xpData.testnetCampaignParticipant,
xpData.enrollDate,
xpData.mintDate,
xpData.generation,
]
);

for (let i = 0; i < tasksId.length; i++) {
for (let i = 0; i < nft.taskIds.length; i++) {
payload = ethers.utils.defaultAbiCoder.encode(
["bytes", "uint256", "bool", "uint256"],
[payload, tasksId[i], tasks[i].completed, tasks[i].count]
[payload, nft.taskIds[i], nft.taskValues[i].completed, nft.taskValues[i].count]
);
}

Expand Down
Loading

0 comments on commit 5108de2

Please sign in to comment.