From bfc26f7f58e9d8ff34b3c1c983ae5c96b1565926 Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Mon, 9 Oct 2023 16:14:23 -0300 Subject: [PATCH 01/15] SC to verify an address and accept an invitation --- .../contracts/zeta-points/Reference.sol | 31 +++++++++++++++++++ .../contracts/zeta-points/Register.sol | 21 +++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 packages/zevm-app-contracts/contracts/zeta-points/Reference.sol create mode 100644 packages/zevm-app-contracts/contracts/zeta-points/Register.sol diff --git a/packages/zevm-app-contracts/contracts/zeta-points/Reference.sol b/packages/zevm-app-contracts/contracts/zeta-points/Reference.sol new file mode 100644 index 00000000..68bbd858 --- /dev/null +++ b/packages/zevm-app-contracts/contracts/zeta-points/Reference.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.7; + +contract Reference { + /* An ECDSA signature. */ + struct Sig { + uint8 v; + bytes32 r; + bytes32 s; + } + + mapping(address => mapping(address => uint256)) public invitations; + + error InvalidInvitation(); + event Invitation(address indexed inviter, address indexed invitee); + + function validateInvitation(address inviter, address invitee, Sig calldata sig) private view { + bytes32 payloadHash = keccak256(abi.encode(inviter, invitee)); + bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash)); + + address actualSigner = ecrecover(messageHash, sig.v, sig.r, sig.s); + if (inviter != actualSigner) revert InvalidInvitation(); + } + + function acceptInvitation(address inviter, Sig calldata sig) external { + if (invitations[inviter][msg.sender] != address(0)) revert InvalidInvitation(); + validateInvitation(inviter, msg.sender, sig); + invitations[inviter][msg.sender] = block.timestamp; + emit Invitation(inviter, msg.sender); + } +} diff --git a/packages/zevm-app-contracts/contracts/zeta-points/Register.sol b/packages/zevm-app-contracts/contracts/zeta-points/Register.sol new file mode 100644 index 00000000..f8ccf7ef --- /dev/null +++ b/packages/zevm-app-contracts/contracts/zeta-points/Register.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.7; + +contract Register { + mapping(address => uint256) public verified; + + event UserVerification(address indexed user, uint256 timestamp); + + function verify() external { + verified[msg.sender] = block.timestamp; + emit UserVerification(msg.sender, block.timestamp); + } + + function isVerified(address addr) external view returns (bool) { + return verified[addr] > 0; + } + + function whenVerified(address addr) external view returns (uint256) { + return verified[addr]; + } +} From baa2e37d9f3cee42a05c3df19113ccdd0c69f5a6 Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Mon, 9 Oct 2023 16:22:01 -0300 Subject: [PATCH 02/15] naming --- .../zeta-points/InvitationManager.sol | 51 +++++++++++++++++++ .../contracts/zeta-points/Reference.sol | 31 ----------- .../contracts/zeta-points/Register.sol | 21 -------- .../zeta-points/UserVerificationRegistry.sol | 22 ++++++++ 4 files changed, 73 insertions(+), 52 deletions(-) create mode 100644 packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol delete mode 100644 packages/zevm-app-contracts/contracts/zeta-points/Reference.sol delete mode 100644 packages/zevm-app-contracts/contracts/zeta-points/Register.sol create mode 100644 packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol diff --git a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol new file mode 100644 index 00000000..f74eb134 --- /dev/null +++ b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.7; + +contract InvitationManager { + /* An ECDSA signature. */ + struct Signature { + uint8 v; + bytes32 r; + bytes32 s; + } + + // Records the timestamp when a particular user accepted an invitation from an inviter. + mapping(address => mapping(address => uint256)) public acceptedInvitationsTimestamp; + + // Store invitees for each inviter + mapping(address => address[]) public inviteeLists; + + error UnrecognizedInvitation(); + error IndexOutOfBounds(); + event InvitationAccepted(address indexed inviter, address indexed invitee, uint256 indexed timestamp); + + function verifySignature(address inviter, address invitee, Signature calldata signature) private pure { + bytes32 payloadHash = keccak256(abi.encode(inviter, invitee)); + bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash)); + + address signerOfMessage = ecrecover(messageHash, signature.v, signature.r, signature.s); + if (inviter != signerOfMessage) revert UnrecognizedInvitation(); + } + + function confirmAndAcceptInvitation(address inviter, Signature calldata signature) external { + if (acceptedInvitationsTimestamp[inviter][msg.sender] != 0) revert UnrecognizedInvitation(); + + verifySignature(inviter, msg.sender, signature); + + acceptedInvitationsTimestamp[inviter][msg.sender] = block.timestamp; + + // Add the invitee to the inviter's list + inviteeLists[inviter].push(msg.sender); + + emit InvitationAccepted(inviter, msg.sender, block.timestamp); + } + + function getInviteeCount(address inviter) external view returns (uint256) { + return inviteeLists[inviter].length; + } + + function getInviteeAtIndex(address inviter, uint256 index) external view returns (address) { + if (index >= inviteeLists[inviter].length) revert IndexOutOfBounds(); + return inviteeLists[inviter][index]; + } +} diff --git a/packages/zevm-app-contracts/contracts/zeta-points/Reference.sol b/packages/zevm-app-contracts/contracts/zeta-points/Reference.sol deleted file mode 100644 index 68bbd858..00000000 --- a/packages/zevm-app-contracts/contracts/zeta-points/Reference.sol +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.7; - -contract Reference { - /* An ECDSA signature. */ - struct Sig { - uint8 v; - bytes32 r; - bytes32 s; - } - - mapping(address => mapping(address => uint256)) public invitations; - - error InvalidInvitation(); - event Invitation(address indexed inviter, address indexed invitee); - - function validateInvitation(address inviter, address invitee, Sig calldata sig) private view { - bytes32 payloadHash = keccak256(abi.encode(inviter, invitee)); - bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash)); - - address actualSigner = ecrecover(messageHash, sig.v, sig.r, sig.s); - if (inviter != actualSigner) revert InvalidInvitation(); - } - - function acceptInvitation(address inviter, Sig calldata sig) external { - if (invitations[inviter][msg.sender] != address(0)) revert InvalidInvitation(); - validateInvitation(inviter, msg.sender, sig); - invitations[inviter][msg.sender] = block.timestamp; - emit Invitation(inviter, msg.sender); - } -} diff --git a/packages/zevm-app-contracts/contracts/zeta-points/Register.sol b/packages/zevm-app-contracts/contracts/zeta-points/Register.sol deleted file mode 100644 index f8ccf7ef..00000000 --- a/packages/zevm-app-contracts/contracts/zeta-points/Register.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.7; - -contract Register { - mapping(address => uint256) public verified; - - event UserVerification(address indexed user, uint256 timestamp); - - function verify() external { - verified[msg.sender] = block.timestamp; - emit UserVerification(msg.sender, block.timestamp); - } - - function isVerified(address addr) external view returns (bool) { - return verified[addr] > 0; - } - - function whenVerified(address addr) external view returns (uint256) { - return verified[addr]; - } -} diff --git a/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol b/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol new file mode 100644 index 00000000..927f6f8f --- /dev/null +++ b/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.7; + +contract UserVerificationRegistry { + // Records the timestamp when a particular user gets verified. + mapping(address => uint256) public userVerificationTimestamps; + + event UserVerified(address indexed userAddress, uint256 verifiedAt); + + function markAsVerified() external { + userVerificationTimestamps[msg.sender] = block.timestamp; + emit UserVerified(msg.sender, block.timestamp); + } + + function hasBeenVerified(address userAddress) external view returns (bool) { + return userVerificationTimestamps[userAddress] > 0; + } + + function getLastVerifiedTimestamp(address userAddress) external view returns (uint256) { + return userVerificationTimestamps[userAddress]; + } +} From 1a61a7510947fdf6fb84613cd30e02bbcd6933ea Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Mon, 9 Oct 2023 16:40:27 -0300 Subject: [PATCH 03/15] add test --- .../test/zeta-points/InvitationManager.ts | 44 +++++++++++++++++++ .../test/zeta-points/test.helpers.ts | 14 ++++++ 2 files changed, 58 insertions(+) create mode 100644 packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts create mode 100644 packages/zevm-app-contracts/test/zeta-points/test.helpers.ts diff --git a/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts new file mode 100644 index 00000000..b068050c --- /dev/null +++ b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts @@ -0,0 +1,44 @@ +import { expect, use } from "chai"; +import { solidity } from "ethereum-waffle"; +use(solidity); +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { ethers } from "hardhat"; + +import type { InvitationManager } from "../../typechain-types"; +import { getInvitationSig } from "./test.helpers"; + +describe("InvitationManager Contract test", () => { + let invitationManager: InvitationManager, inviter: SignerWithAddress, invitee: SignerWithAddress, addrs: SignerWithAddress[]; + + beforeEach(async () => { + [inviter, invitee, ...addrs] = await ethers.getSigners(); + const InvitationManager = await ethers.getContractFactory("InvitationManager"); + //@ts-ignore + invitationManager = await InvitationManager.deploy(); + }); + + describe("True", () => { + it("Should be true", async () => { + expect(true).to.equal(true); + }); + }); + + describe("Invitations test", () => { + it("Should verify an invitation and store it", async () => { + const sig = await getInvitationSig(inviter, invitee.address); + const tx = await invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); + const rec = await tx.wait(); + + const block = await ethers.provider.getBlock(rec.blockNumber); + + const invitation = await invitationManager.acceptedInvitationsTimestamp(inviter.address, invitee.address); + await expect(invitation).to.be.eq(block.timestamp); + }); + + it("Should revert if invitation is invalid", async () => { + const sig = await getInvitationSig(inviter, addrs[0].address); + const tx = invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); + await expect(tx).to.be.revertedWith("UnrecognizedInvitation"); + }); + }); +}); diff --git a/packages/zevm-app-contracts/test/zeta-points/test.helpers.ts b/packages/zevm-app-contracts/test/zeta-points/test.helpers.ts new file mode 100644 index 00000000..38634ecf --- /dev/null +++ b/packages/zevm-app-contracts/test/zeta-points/test.helpers.ts @@ -0,0 +1,14 @@ +//@ts-ignore +import { BigNumber } from "@ethersproject/bignumber"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { ethers, network } from "hardhat"; + +export const getInvitationSig = async (signer: SignerWithAddress, invitee: string) => { + let payload = ethers.utils.defaultAbiCoder.encode(["address", "address"], [signer.address, invitee]); + + let payloadHash = ethers.utils.keccak256(payload); + + // This adds the message prefix + let signature = await signer.signMessage(ethers.utils.arrayify(payloadHash)); + return ethers.utils.splitSignature(signature); +}; From 4a917032f2996f6a82c29a829885c4c0f56045fd Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Mon, 9 Oct 2023 16:42:31 -0300 Subject: [PATCH 04/15] add validation --- .../contracts/zeta-points/UserVerificationRegistry.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol b/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol index 927f6f8f..ce0a503e 100644 --- a/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol +++ b/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol @@ -5,9 +5,15 @@ contract UserVerificationRegistry { // Records the timestamp when a particular user gets verified. mapping(address => uint256) public userVerificationTimestamps; + // Custom errors + error UserAlreadyVerified(); + event UserVerified(address indexed userAddress, uint256 verifiedAt); function markAsVerified() external { + // Check if the user is already verified + if (userVerificationTimestamps[msg.sender] > 0) revert UserAlreadyVerified(); + userVerificationTimestamps[msg.sender] = block.timestamp; emit UserVerified(msg.sender, block.timestamp); } From 6aee482c0b7344548dc974c189fa3df33da1dc9f Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Mon, 9 Oct 2023 16:44:22 -0300 Subject: [PATCH 05/15] naming --- .../contracts/zeta-points/UserVerificationRegistry.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol b/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol index ce0a503e..196e9078 100644 --- a/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol +++ b/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol @@ -22,7 +22,7 @@ contract UserVerificationRegistry { return userVerificationTimestamps[userAddress] > 0; } - function getLastVerifiedTimestamp(address userAddress) external view returns (uint256) { + function getVerifiedTimestamp(address userAddress) external view returns (uint256) { return userVerificationTimestamps[userAddress]; } } From 8f4571f72416dfad69cf11baba6af26c39737a9f Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Tue, 10 Oct 2023 15:26:50 -0300 Subject: [PATCH 06/15] add counters --- .../zeta-points/InvitationManager.sol | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol index f74eb134..5fe7d6ad 100644 --- a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol +++ b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol @@ -15,6 +15,12 @@ contract InvitationManager { // Store invitees for each inviter mapping(address => address[]) public inviteeLists; + // Total invites accepted by day (using the start timestamp of each day as key) + mapping(uint256 => uint256) public totalInvitesByDay; + + // Total invites accepted by inviter by day (using the start timestamp of each day as key) + mapping(address => mapping(uint256 => uint256)) public totalInvitesByInviterByDay; + error UnrecognizedInvitation(); error IndexOutOfBounds(); event InvitationAccepted(address indexed inviter, address indexed invitee, uint256 indexed timestamp); @@ -37,6 +43,11 @@ contract InvitationManager { // Add the invitee to the inviter's list inviteeLists[inviter].push(msg.sender); + uint256 dayStartTimestamp = (block.timestamp / 86400) * 86400; // Normalize to the start of the day + + totalInvitesByDay[dayStartTimestamp]++; + totalInvitesByInviterByDay[inviter][dayStartTimestamp]++; + emit InvitationAccepted(inviter, msg.sender, block.timestamp); } @@ -48,4 +59,12 @@ contract InvitationManager { if (index >= inviteeLists[inviter].length) revert IndexOutOfBounds(); return inviteeLists[inviter][index]; } + + function getTotalInvitesOnDay(uint256 dayStartTimestamp) external view returns (uint256) { + return totalInvitesByDay[dayStartTimestamp]; + } + + function getInvitesByInviterOnDay(address inviter, uint256 dayStartTimestamp) external view returns (uint256) { + return totalInvitesByInviterByDay[inviter][dayStartTimestamp]; + } } From 000bd7f406cb5a2f8a309b697dcd30b4b5ce0bbf Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Wed, 11 Oct 2023 13:28:03 -0300 Subject: [PATCH 07/15] add test --- .../zeta-points/InvitationManager.sol | 5 +- .../scripts/zeta-points/deploy.ts | 39 ++++++++++++++ .../test/zeta-points/InvitationManager.ts | 52 ++++++++++++++++++- .../zeta-points/UserVerificationRegistry.ts | 41 +++++++++++++++ .../test/zeta-points/test.helpers.ts | 4 +- 5 files changed, 134 insertions(+), 7 deletions(-) create mode 100644 packages/zevm-app-contracts/scripts/zeta-points/deploy.ts create mode 100644 packages/zevm-app-contracts/test/zeta-points/UserVerificationRegistry.ts diff --git a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol index 5fe7d6ad..6450d769 100644 --- a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol +++ b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol @@ -23,7 +23,8 @@ contract InvitationManager { error UnrecognizedInvitation(); error IndexOutOfBounds(); - event InvitationAccepted(address indexed inviter, address indexed invitee, uint256 indexed timestamp); + error InvitationAlreadyAccepted(); + event InvitationAccepted(address indexed inviter, address indexed invitee, uint256 timestamp); function verifySignature(address inviter, address invitee, Signature calldata signature) private pure { bytes32 payloadHash = keccak256(abi.encode(inviter, invitee)); @@ -34,7 +35,7 @@ contract InvitationManager { } function confirmAndAcceptInvitation(address inviter, Signature calldata signature) external { - if (acceptedInvitationsTimestamp[inviter][msg.sender] != 0) revert UnrecognizedInvitation(); + if (acceptedInvitationsTimestamp[inviter][msg.sender] != 0) revert InvitationAlreadyAccepted(); verifySignature(inviter, msg.sender, signature); diff --git a/packages/zevm-app-contracts/scripts/zeta-points/deploy.ts b/packages/zevm-app-contracts/scripts/zeta-points/deploy.ts new file mode 100644 index 00000000..ec3422a3 --- /dev/null +++ b/packages/zevm-app-contracts/scripts/zeta-points/deploy.ts @@ -0,0 +1,39 @@ +import { isNetworkName } from "@zetachain/addresses"; +import { saveAddress } from "@zetachain/addresses-tools"; +import { ethers, network } from "hardhat"; + +import { InvitationManager__factory, UserVerificationRegistry__factory } from "../../typechain-types"; + +const networkName = network.name; + +const deployUserVerificationRegistry = async () => { + const UserVerificationRegistryFactory = (await ethers.getContractFactory( + "UserVerificationRegistry" + )) as UserVerificationRegistry__factory; + + const userVerificationRegistry = await UserVerificationRegistryFactory.deploy(); + await userVerificationRegistry.deployed(); + console.log("UserVerificationRegistry deployed to:", userVerificationRegistry.address); + // saveAddress("userVerificationRegistry", userVerificationRegistry.address); +}; + +const invitationManager = async () => { + const InvitationManagerFactory = (await ethers.getContractFactory("InvitationManager")) as InvitationManager__factory; + + const invitationManager = await InvitationManagerFactory.deploy(); + await invitationManager.deployed(); + console.log("InvitationManager deployed to:", invitationManager.address); + // saveAddress("invitationManager", invitationManager.address); +}; + +const main = async () => { + if (!isNetworkName(networkName)) throw new Error("Invalid network name"); + + await deployUserVerificationRegistry(); + await invitationManager(); +}; + +main().catch(error => { + console.error(error); + process.exit(1); +}); diff --git a/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts index b068050c..98ef553d 100644 --- a/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts +++ b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts @@ -4,11 +4,14 @@ use(solidity); import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { ethers } from "hardhat"; -import type { InvitationManager } from "../../typechain-types"; +import { InvitationManager } from "../../typechain-types"; import { getInvitationSig } from "./test.helpers"; describe("InvitationManager Contract test", () => { - let invitationManager: InvitationManager, inviter: SignerWithAddress, invitee: SignerWithAddress, addrs: SignerWithAddress[]; + let invitationManager: InvitationManager, + inviter: SignerWithAddress, + invitee: SignerWithAddress, + addrs: SignerWithAddress[]; beforeEach(async () => { [inviter, invitee, ...addrs] = await ethers.getSigners(); @@ -33,6 +36,9 @@ describe("InvitationManager Contract test", () => { const invitation = await invitationManager.acceptedInvitationsTimestamp(inviter.address, invitee.address); await expect(invitation).to.be.eq(block.timestamp); + + const invitationCount = await invitationManager.getInviteeCount(inviter.address); + await expect(invitationCount).to.be.eq(1); }); it("Should revert if invitation is invalid", async () => { @@ -40,5 +46,47 @@ describe("InvitationManager Contract test", () => { const tx = invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); await expect(tx).to.be.revertedWith("UnrecognizedInvitation"); }); + + it("Should revert if invitation is already accepted", async () => { + const sig = await getInvitationSig(inviter, invitee.address); + await invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); + const tx = invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); + await expect(tx).to.be.revertedWith("InvitationAlreadyAccepted"); + }); + + it("Should count only for today if I just accepted", async () => { + const sig = await getInvitationSig(inviter, invitee.address); + const tx = await invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); + const rec = await tx.wait(); + + const block = await ethers.provider.getBlock(rec.blockNumber); + + const invitation = await invitationManager.acceptedInvitationsTimestamp(inviter.address, invitee.address); + await expect(invitation).to.be.eq(block.timestamp); + + const invitationCount = await invitationManager.getInviteeCount(inviter.address); + await expect(invitationCount).to.be.eq(1); + + const now = block.timestamp; + const todayTimestamp = Math.floor(now / 86400) * 86400; + const invitationCountToday = await invitationManager.getTotalInvitesOnDay(todayTimestamp); + await expect(invitationCountToday).to.be.eq(1); + + const invitationByInviterCountToday = await invitationManager.getInvitesByInviterOnDay( + inviter.address, + todayTimestamp + ); + await expect(invitationByInviterCountToday).to.be.eq(1); + + const yesterdayTimestamp = todayTimestamp - 24 * 60 * 60; + const invitationCountYesterday = await invitationManager.getTotalInvitesOnDay(yesterdayTimestamp); + await expect(invitationCountYesterday).to.be.eq(0); + + const invitationByInviterCountYesterday = await invitationManager.getInvitesByInviterOnDay( + inviter.address, + yesterdayTimestamp + ); + await expect(invitationByInviterCountYesterday).to.be.eq(0); + }); }); }); diff --git a/packages/zevm-app-contracts/test/zeta-points/UserVerificationRegistry.ts b/packages/zevm-app-contracts/test/zeta-points/UserVerificationRegistry.ts new file mode 100644 index 00000000..7bfa5ba3 --- /dev/null +++ b/packages/zevm-app-contracts/test/zeta-points/UserVerificationRegistry.ts @@ -0,0 +1,41 @@ +import { expect, use } from "chai"; +import { solidity } from "ethereum-waffle"; +use(solidity); +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { ethers } from "hardhat"; + +import { UserVerificationRegistry } from "../../typechain-types"; + +describe("UserVerificationRegistry Contract test", () => { + let userVerificationRegistry: UserVerificationRegistry, user: SignerWithAddress, addrs: SignerWithAddress[]; + + beforeEach(async () => { + [user, ...addrs] = await ethers.getSigners(); + const UserVerificationRegistryFactory = await ethers.getContractFactory("UserVerificationRegistry"); + //@ts-ignore + userVerificationRegistry = await UserVerificationRegistryFactory.deploy(); + }); + + describe("True", () => { + it("Should be true", async () => { + expect(true).to.equal(true); + }); + }); + + describe("Vereification test", () => { + it("Should be able to verify a wallet", async () => { + const hasBeenVerified = await userVerificationRegistry.hasBeenVerified(user.address); + expect(hasBeenVerified).to.be.false; + + const tx = await userVerificationRegistry.markAsVerified(); + const receipt = await tx.wait(); + const block = await ethers.provider.getBlock(receipt.blockNumber); + + const hasBeenVerifiedAfter = await userVerificationRegistry.hasBeenVerified(user.address); + expect(hasBeenVerifiedAfter).to.be.true; + + const verification = await userVerificationRegistry.getVerifiedTimestamp(user.address); + expect(verification).to.be.eq(block.timestamp); + }); + }); +}); diff --git a/packages/zevm-app-contracts/test/zeta-points/test.helpers.ts b/packages/zevm-app-contracts/test/zeta-points/test.helpers.ts index 38634ecf..806ab648 100644 --- a/packages/zevm-app-contracts/test/zeta-points/test.helpers.ts +++ b/packages/zevm-app-contracts/test/zeta-points/test.helpers.ts @@ -1,7 +1,5 @@ -//@ts-ignore -import { BigNumber } from "@ethersproject/bignumber"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { ethers, network } from "hardhat"; +import { ethers } from "hardhat"; export const getInvitationSig = async (signer: SignerWithAddress, invitee: string) => { let payload = ethers.utils.defaultAbiCoder.encode(["address", "address"], [signer.address, invitee]); From a47e4eb77302788852dc5b271ba7d8c649bb1950 Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Wed, 11 Oct 2023 16:40:21 -0300 Subject: [PATCH 08/15] update contract to unpersonalized links --- .../contracts/zeta-points/InvitationManager.sol | 6 +++--- .../test/zeta-points/InvitationManager.ts | 10 +++++----- .../test/zeta-points/test.helpers.ts | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol index 6450d769..9d4a847a 100644 --- a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol +++ b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol @@ -26,8 +26,8 @@ contract InvitationManager { error InvitationAlreadyAccepted(); event InvitationAccepted(address indexed inviter, address indexed invitee, uint256 timestamp); - function verifySignature(address inviter, address invitee, Signature calldata signature) private pure { - bytes32 payloadHash = keccak256(abi.encode(inviter, invitee)); + function verifySignature(address inviter, Signature calldata signature) private pure { + bytes32 payloadHash = keccak256(abi.encode(inviter)); bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash)); address signerOfMessage = ecrecover(messageHash, signature.v, signature.r, signature.s); @@ -37,7 +37,7 @@ contract InvitationManager { function confirmAndAcceptInvitation(address inviter, Signature calldata signature) external { if (acceptedInvitationsTimestamp[inviter][msg.sender] != 0) revert InvitationAlreadyAccepted(); - verifySignature(inviter, msg.sender, signature); + verifySignature(inviter, signature); acceptedInvitationsTimestamp[inviter][msg.sender] = block.timestamp; diff --git a/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts index 98ef553d..9477a080 100644 --- a/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts +++ b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts @@ -28,7 +28,7 @@ describe("InvitationManager Contract test", () => { describe("Invitations test", () => { it("Should verify an invitation and store it", async () => { - const sig = await getInvitationSig(inviter, invitee.address); + const sig = await getInvitationSig(inviter); const tx = await invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); const rec = await tx.wait(); @@ -42,20 +42,20 @@ describe("InvitationManager Contract test", () => { }); it("Should revert if invitation is invalid", async () => { - const sig = await getInvitationSig(inviter, addrs[0].address); - const tx = invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); + const sig = await getInvitationSig(inviter); + const tx = invitationManager.connect(invitee).confirmAndAcceptInvitation(addrs[0].address, sig); await expect(tx).to.be.revertedWith("UnrecognizedInvitation"); }); it("Should revert if invitation is already accepted", async () => { - const sig = await getInvitationSig(inviter, invitee.address); + const sig = await getInvitationSig(inviter); await invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); const tx = invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); await expect(tx).to.be.revertedWith("InvitationAlreadyAccepted"); }); it("Should count only for today if I just accepted", async () => { - const sig = await getInvitationSig(inviter, invitee.address); + const sig = await getInvitationSig(inviter); const tx = await invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); const rec = await tx.wait(); diff --git a/packages/zevm-app-contracts/test/zeta-points/test.helpers.ts b/packages/zevm-app-contracts/test/zeta-points/test.helpers.ts index 806ab648..cb758210 100644 --- a/packages/zevm-app-contracts/test/zeta-points/test.helpers.ts +++ b/packages/zevm-app-contracts/test/zeta-points/test.helpers.ts @@ -1,8 +1,8 @@ import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { ethers } from "hardhat"; -export const getInvitationSig = async (signer: SignerWithAddress, invitee: string) => { - let payload = ethers.utils.defaultAbiCoder.encode(["address", "address"], [signer.address, invitee]); +export const getInvitationSig = async (signer: SignerWithAddress) => { + let payload = ethers.utils.defaultAbiCoder.encode(["address"], [signer.address]); let payloadHash = ethers.utils.keccak256(payload); From fde87ea1bb275941c2a7e1750822913473c4c5e5 Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Thu, 19 Oct 2023 15:17:09 -0300 Subject: [PATCH 09/15] add registration logic to manager --- .../zeta-points/InvitationManager.sol | 35 ++++++++++++++++--- .../zeta-points/UserVerificationRegistry.sol | 28 --------------- 2 files changed, 30 insertions(+), 33 deletions(-) delete mode 100644 packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol diff --git a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol index 9d4a847a..2aaed3d4 100644 --- a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol +++ b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol @@ -8,6 +8,8 @@ contract InvitationManager { bytes32 r; bytes32 s; } + // Records the timestamp when a particular user gets verified. + mapping(address => uint256) public userVerificationTimestamps; // Records the timestamp when a particular user accepted an invitation from an inviter. mapping(address => mapping(address => uint256)) public acceptedInvitationsTimestamp; @@ -21,12 +23,35 @@ contract InvitationManager { // Total invites accepted by inviter by day (using the start timestamp of each day as key) mapping(address => mapping(uint256 => uint256)) public totalInvitesByInviterByDay; + error UserAlreadyVerified(); error UnrecognizedInvitation(); error IndexOutOfBounds(); - error InvitationAlreadyAccepted(); + error CanNotInviteYourself(); + + event UserVerified(address indexed userAddress, uint256 verifiedAt); event InvitationAccepted(address indexed inviter, address indexed invitee, uint256 timestamp); - function verifySignature(address inviter, Signature calldata signature) private pure { + function _markAsVerified(address user) internal { + // Check if the user is already verified + if (userVerificationTimestamps[user] > 0) revert UserAlreadyVerified(); + + userVerificationTimestamps[user] = block.timestamp; + emit UserVerified(user, block.timestamp); + } + + function markAsVerified() external { + _markAsVerified(msg.sender); + } + + function hasBeenVerified(address userAddress) external view returns (bool) { + return userVerificationTimestamps[userAddress] > 0; + } + + function getVerifiedTimestamp(address userAddress) external view returns (uint256) { + return userVerificationTimestamps[userAddress]; + } + + function _verifySignature(address inviter, Signature calldata signature) private pure { bytes32 payloadHash = keccak256(abi.encode(inviter)); bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash)); @@ -35,11 +60,11 @@ contract InvitationManager { } function confirmAndAcceptInvitation(address inviter, Signature calldata signature) external { - if (acceptedInvitationsTimestamp[inviter][msg.sender] != 0) revert InvitationAlreadyAccepted(); - - verifySignature(inviter, signature); + if (inviter == msg.sender) revert CanNotInviteYourself(); + _verifySignature(inviter, signature); acceptedInvitationsTimestamp[inviter][msg.sender] = block.timestamp; + _markAsVerified(msg.sender); // Add the invitee to the inviter's list inviteeLists[inviter].push(msg.sender); diff --git a/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol b/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol deleted file mode 100644 index 196e9078..00000000 --- a/packages/zevm-app-contracts/contracts/zeta-points/UserVerificationRegistry.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.7; - -contract UserVerificationRegistry { - // Records the timestamp when a particular user gets verified. - mapping(address => uint256) public userVerificationTimestamps; - - // Custom errors - error UserAlreadyVerified(); - - event UserVerified(address indexed userAddress, uint256 verifiedAt); - - function markAsVerified() external { - // Check if the user is already verified - if (userVerificationTimestamps[msg.sender] > 0) revert UserAlreadyVerified(); - - userVerificationTimestamps[msg.sender] = block.timestamp; - emit UserVerified(msg.sender, block.timestamp); - } - - function hasBeenVerified(address userAddress) external view returns (bool) { - return userVerificationTimestamps[userAddress] > 0; - } - - function getVerifiedTimestamp(address userAddress) external view returns (uint256) { - return userVerificationTimestamps[userAddress]; - } -} From a8735ce35c884fdb50110a787e08890a96eea01b Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Fri, 20 Oct 2023 11:36:43 -0300 Subject: [PATCH 10/15] add validation --- .../contracts/zeta-points/InvitationManager.sol | 1 + .../scripts/zeta-points/deploy.ts | 13 ------------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol index 2aaed3d4..ed3400c9 100644 --- a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol +++ b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol @@ -61,6 +61,7 @@ contract InvitationManager { function confirmAndAcceptInvitation(address inviter, Signature calldata signature) external { if (inviter == msg.sender) revert CanNotInviteYourself(); + if (userVerificationTimestamps[inviter] == 0) revert UnrecognizedInvitation(); _verifySignature(inviter, signature); acceptedInvitationsTimestamp[inviter][msg.sender] = block.timestamp; diff --git a/packages/zevm-app-contracts/scripts/zeta-points/deploy.ts b/packages/zevm-app-contracts/scripts/zeta-points/deploy.ts index ec3422a3..d7cefbbe 100644 --- a/packages/zevm-app-contracts/scripts/zeta-points/deploy.ts +++ b/packages/zevm-app-contracts/scripts/zeta-points/deploy.ts @@ -6,17 +6,6 @@ import { InvitationManager__factory, UserVerificationRegistry__factory } from ". const networkName = network.name; -const deployUserVerificationRegistry = async () => { - const UserVerificationRegistryFactory = (await ethers.getContractFactory( - "UserVerificationRegistry" - )) as UserVerificationRegistry__factory; - - const userVerificationRegistry = await UserVerificationRegistryFactory.deploy(); - await userVerificationRegistry.deployed(); - console.log("UserVerificationRegistry deployed to:", userVerificationRegistry.address); - // saveAddress("userVerificationRegistry", userVerificationRegistry.address); -}; - const invitationManager = async () => { const InvitationManagerFactory = (await ethers.getContractFactory("InvitationManager")) as InvitationManager__factory; @@ -28,8 +17,6 @@ const invitationManager = async () => { const main = async () => { if (!isNetworkName(networkName)) throw new Error("Invalid network name"); - - await deployUserVerificationRegistry(); await invitationManager(); }; From 6d7bc45aee02128204fdd98e565f2ef35f34dfc0 Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Fri, 20 Oct 2023 13:31:55 -0300 Subject: [PATCH 11/15] fix test --- .../test/zeta-points/InvitationManager.ts | 3 ++- .../test/zeta-points/UserVerificationRegistry.ts | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts index 9477a080..4f5a84a7 100644 --- a/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts +++ b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts @@ -18,6 +18,7 @@ describe("InvitationManager Contract test", () => { const InvitationManager = await ethers.getContractFactory("InvitationManager"); //@ts-ignore invitationManager = await InvitationManager.deploy(); + await invitationManager.markAsVerified(); }); describe("True", () => { @@ -51,7 +52,7 @@ describe("InvitationManager Contract test", () => { const sig = await getInvitationSig(inviter); await invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); const tx = invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); - await expect(tx).to.be.revertedWith("InvitationAlreadyAccepted"); + await expect(tx).to.be.revertedWith("UserAlreadyVerified"); }); it("Should count only for today if I just accepted", async () => { diff --git a/packages/zevm-app-contracts/test/zeta-points/UserVerificationRegistry.ts b/packages/zevm-app-contracts/test/zeta-points/UserVerificationRegistry.ts index 7bfa5ba3..edd75e32 100644 --- a/packages/zevm-app-contracts/test/zeta-points/UserVerificationRegistry.ts +++ b/packages/zevm-app-contracts/test/zeta-points/UserVerificationRegistry.ts @@ -4,16 +4,16 @@ use(solidity); import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { ethers } from "hardhat"; -import { UserVerificationRegistry } from "../../typechain-types"; +import { InvitationManager } from "../../typechain-types"; describe("UserVerificationRegistry Contract test", () => { - let userVerificationRegistry: UserVerificationRegistry, user: SignerWithAddress, addrs: SignerWithAddress[]; + let invitationManager: InvitationManager, user: SignerWithAddress, addrs: SignerWithAddress[]; beforeEach(async () => { [user, ...addrs] = await ethers.getSigners(); - const UserVerificationRegistryFactory = await ethers.getContractFactory("UserVerificationRegistry"); + const InvitationManagerFactory = await ethers.getContractFactory("InvitationManager"); //@ts-ignore - userVerificationRegistry = await UserVerificationRegistryFactory.deploy(); + invitationManager = await InvitationManagerFactory.deploy(); }); describe("True", () => { @@ -24,17 +24,17 @@ describe("UserVerificationRegistry Contract test", () => { describe("Vereification test", () => { it("Should be able to verify a wallet", async () => { - const hasBeenVerified = await userVerificationRegistry.hasBeenVerified(user.address); + const hasBeenVerified = await invitationManager.hasBeenVerified(user.address); expect(hasBeenVerified).to.be.false; - const tx = await userVerificationRegistry.markAsVerified(); + const tx = await invitationManager.markAsVerified(); const receipt = await tx.wait(); const block = await ethers.provider.getBlock(receipt.blockNumber); - const hasBeenVerifiedAfter = await userVerificationRegistry.hasBeenVerified(user.address); + const hasBeenVerifiedAfter = await invitationManager.hasBeenVerified(user.address); expect(hasBeenVerifiedAfter).to.be.true; - const verification = await userVerificationRegistry.getVerifiedTimestamp(user.address); + const verification = await invitationManager.getVerifiedTimestamp(user.address); expect(verification).to.be.eq(block.timestamp); }); }); From 8ee07a67f57a083f1c2febd06abb1c0c7ad3bd9b Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Mon, 30 Oct 2023 16:01:07 -0300 Subject: [PATCH 12/15] rename event --- packages/addresses/package.json | 2 +- .../contracts/zeta-points/InvitationManager.sol | 2 +- .../test/zeta-points/InvitationManager.ts | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/addresses/package.json b/packages/addresses/package.json index 0e081e1c..59ab7864 100644 --- a/packages/addresses/package.json +++ b/packages/addresses/package.json @@ -22,4 +22,4 @@ "vite": "^3.1.0", "vite-plugin-dts": "^1.4.1" } -} \ No newline at end of file +} diff --git a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol index ed3400c9..247decf7 100644 --- a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol +++ b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol @@ -29,7 +29,7 @@ contract InvitationManager { error CanNotInviteYourself(); event UserVerified(address indexed userAddress, uint256 verifiedAt); - event InvitationAccepted(address indexed inviter, address indexed invitee, uint256 timestamp); + event InvitationAccepted(address indexed inviter, address indexed invitee, uint256 acceptedAt); function _markAsVerified(address user) internal { // Check if the user is already verified diff --git a/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts index 4f5a84a7..ed515f59 100644 --- a/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts +++ b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts @@ -30,6 +30,10 @@ describe("InvitationManager Contract test", () => { describe("Invitations test", () => { it("Should verify an invitation and store it", async () => { const sig = await getInvitationSig(inviter); + + const hasBeenVerifiedBefore = await invitationManager.hasBeenVerified(invitee.address); + await expect(hasBeenVerifiedBefore).to.be.eq(false); + const tx = await invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); const rec = await tx.wait(); @@ -40,6 +44,9 @@ describe("InvitationManager Contract test", () => { const invitationCount = await invitationManager.getInviteeCount(inviter.address); await expect(invitationCount).to.be.eq(1); + + const hasBeenVerifiedAfter = await invitationManager.hasBeenVerified(invitee.address); + await expect(hasBeenVerifiedAfter).to.be.eq(true); }); it("Should revert if invitation is invalid", async () => { @@ -48,6 +55,12 @@ describe("InvitationManager Contract test", () => { await expect(tx).to.be.revertedWith("UnrecognizedInvitation"); }); + it("Should revert if inviter has not been verified", async () => { + const sig = await getInvitationSig(addrs[0]); + const tx = invitationManager.connect(invitee).confirmAndAcceptInvitation(addrs[0].address, sig); + await expect(tx).to.be.revertedWith("UnrecognizedInvitation"); + }); + it("Should revert if invitation is already accepted", async () => { const sig = await getInvitationSig(inviter); await invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); From 958e3d2805373018921f666feb451ff9dd4b73e0 Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Tue, 31 Oct 2023 12:23:58 -0300 Subject: [PATCH 13/15] rename messageSigner --- .../contracts/zeta-points/InvitationManager.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol index 247decf7..85ef8215 100644 --- a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol +++ b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol @@ -55,8 +55,8 @@ contract InvitationManager { bytes32 payloadHash = keccak256(abi.encode(inviter)); bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", payloadHash)); - address signerOfMessage = ecrecover(messageHash, signature.v, signature.r, signature.s); - if (inviter != signerOfMessage) revert UnrecognizedInvitation(); + address messageSigner = ecrecover(messageHash, signature.v, signature.r, signature.s); + if (inviter != messageSigner) revert UnrecognizedInvitation(); } function confirmAndAcceptInvitation(address inviter, Signature calldata signature) external { From c780aa42ded4545769c15e750ac95303fc712072 Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Mon, 13 Nov 2023 16:11:23 -0300 Subject: [PATCH 14/15] add index to event and test --- .../zeta-points/InvitationManager.sol | 4 +-- .../test/zeta-points/InvitationManager.ts | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol index 85ef8215..50f43173 100644 --- a/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol +++ b/packages/zevm-app-contracts/contracts/zeta-points/InvitationManager.sol @@ -29,7 +29,7 @@ contract InvitationManager { error CanNotInviteYourself(); event UserVerified(address indexed userAddress, uint256 verifiedAt); - event InvitationAccepted(address indexed inviter, address indexed invitee, uint256 acceptedAt); + event InvitationAccepted(address indexed inviter, address indexed invitee, uint256 index, uint256 acceptedAt); function _markAsVerified(address user) internal { // Check if the user is already verified @@ -75,7 +75,7 @@ contract InvitationManager { totalInvitesByDay[dayStartTimestamp]++; totalInvitesByInviterByDay[inviter][dayStartTimestamp]++; - emit InvitationAccepted(inviter, msg.sender, block.timestamp); + emit InvitationAccepted(inviter, msg.sender, inviteeLists[inviter].length - 1, block.timestamp); } function getInviteeCount(address inviter) external view returns (uint256) { diff --git a/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts index ed515f59..e5aaa350 100644 --- a/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts +++ b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts @@ -102,5 +102,32 @@ describe("InvitationManager Contract test", () => { ); await expect(invitationByInviterCountYesterday).to.be.eq(0); }); + + it("Should emit the right event when invitation is accepted", async () => { + const sig = await getInvitationSig(inviter); + + const hasBeenVerifiedBefore = await invitationManager.hasBeenVerified(invitee.address); + await expect(hasBeenVerifiedBefore).to.be.eq(false); + + const tx = await invitationManager.connect(invitee).confirmAndAcceptInvitation(inviter.address, sig); + const rec = await tx.wait(); + const event = rec.events?.find(e => e.event === "InvitationAccepted"); + const block = await ethers.provider.getBlock(rec.blockNumber); + + await expect(event?.args?.inviter).to.be.eq(inviter.address); + await expect(event?.args?.invitee).to.be.eq(invitee.address); + await expect(event?.args?.index).to.be.eq(0); + await expect(event?.args?.acceptedAt).to.be.eq(block.timestamp); + + const tx2 = await invitationManager.connect(addrs[0]).confirmAndAcceptInvitation(inviter.address, sig); + const rec2 = await tx2.wait(); + const event2 = rec2.events?.find(e => e.event === "InvitationAccepted"); + const block2 = await ethers.provider.getBlock(rec2.blockNumber); + + await expect(event2?.args?.inviter).to.be.eq(inviter.address); + await expect(event2?.args?.invitee).to.be.eq(addrs[0].address); + await expect(event2?.args?.index).to.be.eq(1); + await expect(event2?.args?.acceptedAt).to.be.eq(block2.timestamp); + }); }); }); From 0dfa49ba1f3ff69259fe82d521967d70ee413421 Mon Sep 17 00:00:00 2001 From: Andres Aiello Date: Mon, 13 Nov 2023 16:15:32 -0300 Subject: [PATCH 15/15] improve test --- .../test/zeta-points/InvitationManager.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts index e5aaa350..b169e037 100644 --- a/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts +++ b/packages/zevm-app-contracts/test/zeta-points/InvitationManager.ts @@ -114,10 +114,12 @@ describe("InvitationManager Contract test", () => { const event = rec.events?.find(e => e.event === "InvitationAccepted"); const block = await ethers.provider.getBlock(rec.blockNumber); - await expect(event?.args?.inviter).to.be.eq(inviter.address); - await expect(event?.args?.invitee).to.be.eq(invitee.address); - await expect(event?.args?.index).to.be.eq(0); - await expect(event?.args?.acceptedAt).to.be.eq(block.timestamp); + expect(event?.args?.inviter).to.be.eq(inviter.address); + expect(event?.args?.invitee).to.be.eq(invitee.address); + expect(event?.args?.index).to.be.eq(0); + expect(event?.args?.acceptedAt).to.be.eq(block.timestamp); + const inviteeByIndex = await invitationManager.getInviteeAtIndex(inviter.address, event?.args?.index); + expect(inviteeByIndex).to.be.eq(invitee.address); const tx2 = await invitationManager.connect(addrs[0]).confirmAndAcceptInvitation(inviter.address, sig); const rec2 = await tx2.wait(); @@ -128,6 +130,8 @@ describe("InvitationManager Contract test", () => { await expect(event2?.args?.invitee).to.be.eq(addrs[0].address); await expect(event2?.args?.index).to.be.eq(1); await expect(event2?.args?.acceptedAt).to.be.eq(block2.timestamp); + const inviteeByIndex2 = await invitationManager.getInviteeAtIndex(inviter.address, event2?.args?.index); + expect(inviteeByIndex2).to.be.eq(addrs[0].address); }); }); });