Skip to content

Commit

Permalink
feat: Add signature expiration
Browse files Browse the repository at this point in the history
  • Loading branch information
andresaiello committed Aug 22, 2024
1 parent ac82253 commit a5b6ea2
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 25 deletions.
8 changes: 4 additions & 4 deletions packages/zevm-app-contracts/contracts/xp-nft/xpNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@ contract ZetaXP is ERC721Upgradeable, OwnableUpgradeable {
struct UpdateData {
address to;
Signature signature;
uint256 signatureExpiration;
uint256 sigTimestamp;
uint256 signedUp;
bytes32 tag;
}

mapping(uint256 => uint256) public lastUpdateTimestampByTokenId;
mapping(uint256 => uint256) public signedUpByTokenId;
mapping(uint256 => bytes32) public tagByTokenId;
mapping(address => mapping(bytes32 => uint256)) public tokenByUserTag;

Expand All @@ -39,6 +38,7 @@ contract ZetaXP is ERC721Upgradeable, OwnableUpgradeable {
event NFTUpdated(address indexed sender, uint256 indexed tokenId, bytes32 tag);

error InvalidSigner();
error SignatureExpired();
error InvalidAddress();
error LengthMismatch();
error TransferNotAllowed();
Expand Down Expand Up @@ -111,15 +111,16 @@ contract ZetaXP is ERC721Upgradeable, OwnableUpgradeable {
);

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

// Function to compute the hash of the data and tasks for a token
function _calculateHash(UpdateData memory updateData) private pure returns (bytes32) {
bytes memory encodedData = abi.encode(
updateData.to,
updateData.signatureExpiration,
updateData.sigTimestamp,
updateData.signedUp,
updateData.tag
);

Expand All @@ -129,7 +130,6 @@ contract ZetaXP is ERC721Upgradeable, OwnableUpgradeable {
function _updateNFT(uint256 tokenId, UpdateData memory updateData) internal {
_verify(tokenId, updateData);
lastUpdateTimestampByTokenId[tokenId] = updateData.sigTimestamp;
signedUpByTokenId[tokenId] = updateData.signedUp;
tagByTokenId[tokenId] = updateData.tag;
tokenByUserTag[updateData.to][updateData.tag] = tokenId;
}
Expand Down
12 changes: 9 additions & 3 deletions packages/zevm-app-contracts/test/xp-nft/test.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,27 @@ export interface Signature {
}

export interface NFT {
signedUp: number;
tag: string;
to: string;
}

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

export const getSignature = async (signer: SignerWithAddress, timestamp: number, to: string, nft: NFT) => {
export const getSignature = async (
signer: SignerWithAddress,
signatureExpiration: number,
timestamp: number,
to: string,
nft: NFT
) => {
let payload = ethers.utils.defaultAbiCoder.encode(
["address", "uint256", "uint256", "bytes32"],
[to, timestamp, nft.signedUp, nft.tag]
[to, signatureExpiration, timestamp, nft.tag]
);

const payloadHash = ethers.utils.keccak256(payload);
Expand Down
58 changes: 40 additions & 18 deletions packages/zevm-app-contracts/test/xp-nft/xp-nft.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { expect, use } from "chai";
import { solidity } from "ethereum-waffle";
use(solidity);
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import exp from "constants";
import { expect } from "chai";
import { ethers, upgrades } from "hardhat";

import { ZetaXP } from "../../typechain-types";
Expand Down Expand Up @@ -30,7 +27,6 @@ describe("XP NFT Contract test", () => {
const tag = ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode(["string"], ["XP_NFT"]));

sampleNFT = {
signedUp: 1234,
tag,
to: user.address,
};
Expand All @@ -52,13 +48,15 @@ describe("XP NFT Contract test", () => {
it("Should mint an NFT", async () => {
const currentBlock = await ethers.provider.getBlock("latest");
const sigTimestamp = currentBlock.timestamp;
const signatureExpiration = sigTimestamp + 1000;

const signature = await getSignature(signer, sigTimestamp, sampleNFT.to, sampleNFT);
const signature = await getSignature(signer, signatureExpiration, sigTimestamp, sampleNFT.to, sampleNFT);

const nftParams: UpdateParam = {
...sampleNFT,
sigTimestamp,
signature,
signatureExpiration,
} as UpdateParam;

const tx = await zetaXP.mintNFT(nftParams);
Expand All @@ -71,13 +69,15 @@ describe("XP NFT Contract test", () => {
it("Should emit event on minting", async () => {
const currentBlock = await ethers.provider.getBlock("latest");
const sigTimestamp = currentBlock.timestamp;
const signatureExpiration = sigTimestamp + 1000;

const signature = await getSignature(signer, sigTimestamp, sampleNFT.to, sampleNFT);
const signature = await getSignature(signer, signatureExpiration, sigTimestamp, sampleNFT.to, sampleNFT);

const nftParams: UpdateParam = {
...sampleNFT,
sigTimestamp,
signature,
signatureExpiration,
} as UpdateParam;
const tx = zetaXP.mintNFT(nftParams);
await expect(tx).to.emit(zetaXP, "NFTMinted").withArgs(user.address, 1, sampleNFT.tag);
Expand All @@ -86,13 +86,15 @@ describe("XP NFT Contract test", () => {
it("Should revert if signature is not correct", async () => {
const currentBlock = await ethers.provider.getBlock("latest");
const sigTimestamp = currentBlock.timestamp;
const signatureExpiration = sigTimestamp + 1000;

const signature = await getSignature(addrs[0], sigTimestamp, sampleNFT.to, sampleNFT);
const signature = await getSignature(addrs[0], signatureExpiration, sigTimestamp, sampleNFT.to, sampleNFT);

const nftParams: UpdateParam = {
...sampleNFT,
sigTimestamp,
signature,
signatureExpiration,
} as UpdateParam;

const tx = zetaXP.mintNFT(nftParams);
Expand All @@ -104,13 +106,15 @@ describe("XP NFT Contract test", () => {
{
const currentBlock = await ethers.provider.getBlock("latest");
const sigTimestamp = currentBlock.timestamp;
const signatureExpiration = sigTimestamp + 1000;

const signature = await getSignature(signer, sigTimestamp, sampleNFT.to, sampleNFT);
const signature = await getSignature(signer, signatureExpiration, sigTimestamp, sampleNFT.to, sampleNFT);

const nftParams: UpdateParam = {
...sampleNFT,
sigTimestamp,
signature,
signatureExpiration,
} as UpdateParam;

const tx = await zetaXP.mintNFT(nftParams);
Expand All @@ -123,13 +127,15 @@ describe("XP NFT Contract test", () => {
{
const currentBlock = await ethers.provider.getBlock("latest");
const sigTimestamp = currentBlock.timestamp;
const signatureExpiration = sigTimestamp + 1000;

const signature = await getSignature(signer, sigTimestamp, sampleNFT.to, updatedSampleNFT);
const signature = await getSignature(signer, signatureExpiration, sigTimestamp, sampleNFT.to, updatedSampleNFT);

const nftParams: UpdateParam = {
...updatedSampleNFT,
sigTimestamp,
signature,
signatureExpiration,
} as UpdateParam;

await zetaXP.updateNFT(tokenId, nftParams);
Expand All @@ -146,13 +152,15 @@ describe("XP NFT Contract test", () => {
{
const currentBlock = await ethers.provider.getBlock("latest");
const sigTimestamp = currentBlock.timestamp;
const signatureExpiration = sigTimestamp + 1000;

const signature = await getSignature(signer, sigTimestamp, sampleNFT.to, sampleNFT);
const signature = await getSignature(signer, signatureExpiration, sigTimestamp, sampleNFT.to, sampleNFT);

const nftParams: UpdateParam = {
...sampleNFT,
sigTimestamp,
signature,
signatureExpiration,
} as UpdateParam;

await zetaXP.mintNFT(nftParams);
Expand All @@ -165,13 +173,15 @@ describe("XP NFT Contract test", () => {
{
const currentBlock = await ethers.provider.getBlock("latest");
const sigTimestamp = currentBlock.timestamp;
const signatureExpiration = sigTimestamp + 1000;

const signature = await getSignature(signer, sigTimestamp, sampleNFT.to, sampleNFT);
const signature = await getSignature(signer, signatureExpiration, sigTimestamp, sampleNFT.to, sampleNFT);

const nftParams: UpdateParam = {
...sampleNFT,
sigTimestamp,
signature,
signatureExpiration,
} as UpdateParam;

await zetaXP.mintNFT(nftParams);
Expand All @@ -189,13 +199,15 @@ describe("XP NFT Contract test", () => {
};
const currentBlock = await ethers.provider.getBlock("latest");
const sigTimestamp = currentBlock.timestamp;
const signatureExpiration = sigTimestamp + 1000;

const signature = await getSignature(signer, sigTimestamp, user.address, sampleNFT2);
const signature = await getSignature(signer, signatureExpiration, sigTimestamp, user.address, sampleNFT2);

const nftParams: UpdateParam = {
...sampleNFT2,
sigTimestamp,
signature,
signatureExpiration,
} as UpdateParam;

await zetaXP.mintNFT(nftParams);
Expand All @@ -216,13 +228,15 @@ describe("XP NFT Contract test", () => {
{
const currentBlock = await ethers.provider.getBlock("latest");
const sigTimestamp = currentBlock.timestamp;
const signatureExpiration = sigTimestamp + 1000;

const signature = await getSignature(signer, sigTimestamp, sampleNFT.to, sampleNFT);
const signature = await getSignature(signer, signatureExpiration, sigTimestamp, sampleNFT.to, sampleNFT);

const nftParams: UpdateParam = {
...sampleNFT,
sigTimestamp,
signature,
signatureExpiration,
} as UpdateParam;

await zetaXP.mintNFT(nftParams);
Expand All @@ -234,13 +248,15 @@ describe("XP NFT Contract test", () => {
it("Should revert if try to use same signature twice", async () => {
const currentBlock = await ethers.provider.getBlock("latest");
const sigTimestamp = currentBlock.timestamp;
const signatureExpiration = sigTimestamp + 1000;

const signature = await getSignature(signer, sigTimestamp, sampleNFT.to, sampleNFT);
const signature = await getSignature(signer, signatureExpiration, sigTimestamp, sampleNFT.to, sampleNFT);

const nftParams: UpdateParam = {
...sampleNFT,
sigTimestamp,
signature,
signatureExpiration,
} as UpdateParam;

const tx = await zetaXP.mintNFT(nftParams);
Expand All @@ -266,13 +282,15 @@ describe("XP NFT Contract test", () => {
{
const currentBlock = await ethers.provider.getBlock("latest");
const sigTimestamp = currentBlock.timestamp;
const signatureExpiration = sigTimestamp + 1000;

const signature = await getSignature(signer, sigTimestamp, sampleNFT.to, sampleNFT);
const signature = await getSignature(signer, signatureExpiration, sigTimestamp, sampleNFT.to, sampleNFT);

const nftParams: UpdateParam = {
...sampleNFT,
sigTimestamp,
signature,
signatureExpiration,
} as UpdateParam;

await zetaXP.mintNFT(nftParams);
Expand All @@ -285,13 +303,15 @@ describe("XP NFT Contract test", () => {
};
const currentBlock = await ethers.provider.getBlock("latest");
const sigTimestamp = currentBlock.timestamp;
const signatureExpiration = sigTimestamp + 1000;

const signature = await getSignature(signer, sigTimestamp, user.address, sampleNFT2);
const signature = await getSignature(signer, signatureExpiration, sigTimestamp, user.address, sampleNFT2);

const nftParams: UpdateParam = {
...sampleNFT2,
sigTimestamp,
signature,
signatureExpiration,
} as UpdateParam;

const tx = zetaXP.mintNFT(nftParams);
Expand All @@ -302,13 +322,15 @@ describe("XP NFT Contract test", () => {
it("Should query by tag and by user", async () => {
const currentBlock = await ethers.provider.getBlock("latest");
const sigTimestamp = currentBlock.timestamp;
const signatureExpiration = sigTimestamp + 1000;

const signature = await getSignature(signer, sigTimestamp, sampleNFT.to, sampleNFT);
const signature = await getSignature(signer, signatureExpiration, sigTimestamp, sampleNFT.to, sampleNFT);

const nftParams: UpdateParam = {
...sampleNFT,
sigTimestamp,
signature,
signatureExpiration,
} as UpdateParam;

const tx = await zetaXP.mintNFT(nftParams);
Expand Down

0 comments on commit a5b6ea2

Please sign in to comment.