Skip to content

Commit

Permalink
Merge pull request #82 from talentprotocol/passport-contract
Browse files Browse the repository at this point in the history
Add passport contract
  • Loading branch information
RubenSousaDinis committed Apr 18, 2024
2 parents a8b4d5a + b1b67b8 commit d6eb2e1
Show file tree
Hide file tree
Showing 10 changed files with 956 additions and 8 deletions.
250 changes: 250 additions & 0 deletions contracts/passport/PassportRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/security/Pausable.sol";
import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol";

contract PassportRegistry is Ownable, Pausable {
using Counters for Counters.Counter;

// wallet => passport id
mapping(address => uint256) public passportId;

// passport id => wallet
mapping(uint256 => address) public idPassport;

// wallet => bool
mapping(address => bool) public walletActive;

// id => bool
mapping(uint256 => bool) public idActive;

// id => source
mapping(uint256 => string) public idSource;

// source => # passports
mapping(string => uint256) public sourcePassports;

// Total number of passports created
Counters.Counter public totalCreates;

// Total number of passports sequencially created
Counters.Counter public totalSequencialCreates;

// Total number of passports created by admins
Counters.Counter public totalAdminsCreates;

// Total number of passport transfers
Counters.Counter public totalPassportTransfers;

// The next id to be issued
uint256 private _nextSequencialPassportId;

// Smart contract id in sequencial mode
bool private _sequencial;

// A new passport has been created
event Create(address indexed wallet, uint256 passportId, string source);

// A passport has been tranfered
event Transfer(uint256 oldPassportId, uint256 newPassportId, address indexed oldWallet, address indexed newWallet);

// A passport has been deactivated
event Deactivate(address indexed wallet, uint256 passportId);

// A passport has been activated
event Activate(address indexed wallet, uint256 passportId);

// Passport generation mode changed
event PassportGenerationChanged(bool sequencial, uint256 nextSequencialPassportId);

/**
* @dev Modifier to make a function callable only when the contract is in sequencial mode.
*
* Requirements:
*
* - The contract must be in sequencial mode.
*/
modifier whenSequencialGeneration() {
require(sequencial(), "Admin generation mode");
_;
}

/**
* @dev Modifier to make a function callable only when the contract is in admin generation mode.
*
* Requirements:
*
* - The contract must be in admin generation mode.
*/
modifier whenAdminGeneration() {
require(!sequencial(), "Sequencial generation mode");
_;
}

constructor(address contractOwner) {
transferOwnership(contractOwner);
_sequencial = false;
}

function create(string memory source) public whenNotPaused whenSequencialGeneration {
require(passportId[msg.sender] == 0, "Passport already exists");

totalSequencialCreates.increment();

_create(msg.sender, _nextSequencialPassportId, source);
_nextSequencialPassportId += 1;
}

function adminCreate(
string memory source,
address wallet,
uint256 id
) public onlyOwner whenNotPaused whenAdminGeneration {
require(passportId[wallet] == 0, "Passport already exists");

totalAdminsCreates.increment();

_create(wallet, id, source);
}

/**
* @notice Transfer the passport id of the msg.sender to the newWallet.
* @dev Can only be called by the passport owner.
*/
function transfer(address newWallet) public whenNotPaused {
uint256 id = passportId[msg.sender];
uint256 newWalletId = passportId[newWallet];
require(id != 0, "Passport does not exist");
require(newWalletId == 0, "Wallet passed already has a passport");

passportId[msg.sender] = 0;
passportId[newWallet] = id;
idPassport[id] = newWallet;
walletActive[msg.sender] = false;
walletActive[newWallet] = true;
totalPassportTransfers.increment();

emit Transfer(id, id, msg.sender, newWallet);
}

// Admin

/**
* @notice Change the wallet passport id to a new one.
* @dev Can only be called by the owner.
*/
function adminTransfer(address wallet, uint256 id) public onlyOwner {
uint256 oldId = passportId[wallet];
address idOwner = idPassport[id];
require(oldId != 0, "Wallet does not have a passport to transfer from");
require(idOwner == address(0), "New passport id already has a owner");

string memory source = idSource[oldId];
idSource[id] = source;
idSource[oldId] = "";
passportId[wallet] = id;
idPassport[oldId] = address(0);
walletActive[wallet] = true;
idActive[id] = true;
idActive[oldId] = false;

totalPassportTransfers.increment();

emit Transfer(oldId, id, wallet, wallet);
}

/**
* @notice Activates the passport of a given walley.
* @dev Can only be called by the owner.
*/
function activate(address wallet) public whenNotPaused onlyOwner {
require(passportId[wallet] != 0, "Passport must exist");
require(walletActive[wallet] == false, "Passport must be inactive");

uint256 id = passportId[wallet];

walletActive[wallet] = true;
idActive[id] = true;

// emit event
emit Activate(wallet, id);
}

/**
* @notice Deactivates the passport of a given walley.
* @dev Can only be called by the owner.
*/
function deactivate(address wallet) public whenNotPaused onlyOwner {
require(passportId[wallet] != 0, "Passport must exist");
require(walletActive[wallet] == true, "Passport must be active");

uint256 id = passportId[wallet];

walletActive[wallet] = false;
idActive[id] = false;

// emit event
emit Deactivate(wallet, id);
}

/**
* @notice Pauses the contract, disabling future creations.
* @dev Can only be called by the owner.
*/
function pause() public whenNotPaused onlyOwner {
_pause();
}

/**
* @notice Enables the contract, enabling new creations.
* @dev Can only be called by the owner.
*/
function unpause() public whenPaused onlyOwner {
_unpause();
}

/**
* @notice Changes the contract generation mode.
* @dev Can only be called by the owner.
*/
function setGenerationMode(bool sequencialFlag, uint256 nextSequencialPassportId) public onlyOwner {
_sequencial = sequencialFlag;
_nextSequencialPassportId = nextSequencialPassportId;

emit PassportGenerationChanged(sequencialFlag, nextSequencialPassportId);
}

/**
* @dev Returns true if the contract is in sequencial mode, and false otherwise.
*/
function sequencial() public view virtual returns (bool) {
return _sequencial;
}

/**
* @dev Returns the next id to be generated.
*/
function nextId() public view virtual returns (uint256) {
return _nextSequencialPassportId;
}

// private

function _create(address wallet, uint256 id, string memory source) private {
require(idPassport[id] == address(0), "Passport id already issued");

totalCreates.increment();

idPassport[id] = wallet;
passportId[wallet] = id;
walletActive[wallet] = true;
idActive[id] = true;
idSource[id] = source;
sourcePassports[source] = SafeMath.add(sourcePassports[source], 1);
// emit event
emit Create(wallet, id, source);
}
}
39 changes: 35 additions & 4 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { task } from "hardhat/config";

import "@typechain/hardhat";
import "@nomiclabs/hardhat-ethers";
import "@nomicfoundation/hardhat-viem";
import "@nomiclabs/hardhat-etherscan";
import "@nomiclabs/hardhat-waffle";
import "@openzeppelin/hardhat-upgrades";
Expand All @@ -13,11 +14,11 @@ dotenv.config();

import type { HardhatUserConfig } from "hardhat/config";

// const deployer = {
// mnemonic: process.env.MNEMONIC || "test test test test test test test test test test test junk",
// };
const deployer = {
mnemonic: process.env.MNEMONIC || "test test test test test test test test test test test junk",
};

const deployer = [process.env.PK_1 || "0x1111111111111111111111111111111111111111111111111111111111111111"];
// const deployer = [""];

task("accounts", "Prints the list of accounts", async (args, hre) => {
const accounts = await hre.ethers.getSigners();
Expand Down Expand Up @@ -59,6 +60,18 @@ const config: HardhatUserConfig = {
chainId: 137,
gasMultiplier: 1.5,
},
baseSepolia: {
url: "https://api.developer.coinbase.com/rpc/v1/base-sepolia/Ip9cOQPtBOm81rN2I9_1rBiMXOfKBxii",
accounts: deployer,
chainId: 84532,
gasMultiplier: 1.5,
},
base: {
url: "https://api.developer.coinbase.com/rpc/v1/base/Ip9cOQPtBOm81rN2I9_1rBiMXOfKBxii",
accounts: deployer,
chainId: 8453,
gasMultiplier: 1.5,
},
},
gasReporter: {
currency: "ETH",
Expand All @@ -70,6 +83,8 @@ const config: HardhatUserConfig = {
alfajores: process.env.CELO_API_KEY || "",
polygon: process.env.POLYGON_API_KEY || "",
polygonMumbai: process.env.POLYGON_API_KEY || "",
base: "",
baseSepolia: "",
},
// Custom chains that are not supported by default
customChains: [
Expand All @@ -89,6 +104,22 @@ const config: HardhatUserConfig = {
browserURL: "https://celoscan.io/",
},
},
{
network: "baseSepolia",
chainId: 84532,
urls: {
apiURL: "https://api-sepolia.basescan.org/api",
browserURL: "https://sepolia.basescan.org",
},
},
{
network: "base",
chainId: 8453,
urls: {
apiURL: "https://api.basescan.org/api",
browserURL: "https://basescan.org",
},
},
],
},
};
Expand Down
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@ethersproject/abi": "^5.0.0",
"@ethersproject/bytes": "^5.0.0",
"@ethersproject/providers": "^5.0.0",
"@nomicfoundation/hardhat-viem": "^2.0.0",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-ganache": "^2.0.0",
"@nomiclabs/hardhat-waffle": "^2.0.1",
Expand All @@ -25,14 +26,18 @@
"nft.storage": "^7.0.0",
"node-fetch": "3.3.1",
"openzeppelin-solidity": "^4.2.0",
"permissionless": "^0.1.4",
"solhint": "^3.3.6",
"solidity-coverage": "^0.8.2",
"solidity-docgen": "^0.5.16",
"ts-node": "^10.8.2",
"typechain": "^8.1.1",
"typescript": "^4.4.2"
"typescript": "^4.4.2",
"viem": "2.7.16"
},
"devDependencies": {
"@nomicfoundation/hardhat-chai-matchers": "^1.0.6",
"@nomicfoundation/hardhat-network-helpers": "^1.0.8",
"@nomiclabs/hardhat-etherscan": "^3.1.7",
"@types/chai": "^4.3.4",
"@types/chai-as-promised": "^7.1.4",
Expand All @@ -46,10 +51,7 @@
"dotenv": "^16.0.3",
"eslint": "^8.19.0",
"ethereum-waffle": "^3.4.0",
"@nomicfoundation/hardhat-chai-matchers": "^1.0.6",
"@nomicfoundation/hardhat-network-helpers": "^1.0.8",
"hardhat-celo": "^0.0.4",
"@nomiclabs/hardhat-etherscan": "^3.1.7",
"prettier": "^2.4.1",
"prettier-plugin-solidity": "^1.0.0-beta.18",
"solc": "^0.8.17"
Expand Down
25 changes: 25 additions & 0 deletions scripts/passport/deployPassportRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ethers, network } from "hardhat";

import { deployPassport } from "../shared";

async function main() {
console.log(`Deploying passport registry at ${network.name}`);

const [admin] = await ethers.getSigners();

console.log(`Admin will be ${admin.address}`);

const passport = await deployPassport(admin.address);

console.log(`Passport Address: ${passport.address}`);
console.log(`Passport owner: ${await passport.owner()}`);

console.log("Done");
}

main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Loading

0 comments on commit d6eb2e1

Please sign in to comment.