Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contracts/passport/SmartBuilderScore.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import "./PassportBuilderScore.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

// Deprecated in favor of TalentBuilderScorer
contract SmartBuilderScore is Ownable {
using ECDSA for bytes32;

Expand Down
111 changes: 111 additions & 0 deletions contracts/passport/TalentBuilderScore.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import "./PassportBuilderScore.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract TalentBuilderScore is Ownable {
using ECDSA for bytes32;

address public trustedSigner;
address public feeReceiver;
PassportBuilderScore public passportBuilderScore;
PassportRegistry public passportRegistry;
uint256 public cost = 0.0001 ether;

event BuilderScoreSet(address indexed user, uint256 score, uint256 talentId);

bool public enabled;

constructor(
address _trustedSigner,
address _passportBuilderScoreAddress,
address _passportRegistryAddress,
address _feeReceiver
) Ownable(_trustedSigner) {
trustedSigner = _trustedSigner;
passportBuilderScore = PassportBuilderScore(_passportBuilderScoreAddress);
passportRegistry = PassportRegistry(_passportRegistryAddress);
feeReceiver = _feeReceiver;
enabled = true;
}

/**
* @notice Changes the owner of passport registry.
* @param _newOwner The new owner of passport registry.
* @dev Can only be called by the owner.
*/
function setPassportRegistryOwner(address _newOwner) public onlyOwner {
passportRegistry.transferOwnership(_newOwner);
}

/**
* @notice Enables or disables the SmartBuilderScore contract.
* @param _enabled Whether the SmartBuilderScore contract should be enabled.
* @dev Can only be called by the owner.
*/
function setEnabled(bool _enabled) public onlyOwner {
enabled = _enabled;
}

/**
* @notice Disables the SmartBuilderScore contract.
* @dev Can only be called by the owner.
*/
function setDisabled() public onlyOwner {
enabled = false;
}

/**
* @notice Sets the cost of adding a score.
* @param _cost The cost of adding a score.
* @dev Can only be called by the owner.
*/
function setCost(uint256 _cost) public onlyOwner {
cost = _cost;
}

/**
* @notice Updates the fee receiver address.
* @param _feeReceiver The new fee receiver address.
* @dev Can only be called by the owner.
*/
function updateReceiver(address _feeReceiver) public onlyOwner {
feeReceiver = _feeReceiver;
}

/**
* @notice Creates an attestation if the provided number is signed by the trusted signer.
* @param score The number to be attested.
* @param talentId The number of the talent profile to receive the attestation.
* @param wallet The wallet to receive the attestation.
* @param signature The signature of the trusted signer.
*/
function addScore(uint256 score, uint256 talentId, address wallet, bytes memory signature) public payable {
require(enabled, "Setting the Builder Score is disabled for this contract");
// Ensure the caller has paid the required fee
require(msg.value >= cost, "Insufficient payment");
// Hash the number
bytes32 numberHash = keccak256(abi.encodePacked(score, talentId, wallet));

// Recover the address that signed the hash
address signer = MessageHashUtils.toEthSignedMessageHash(numberHash).recover(signature);

// Ensure the signer is the trusted signer
require(signer == trustedSigner, "Invalid signature");

// Transfer fee to fee receiver
payable(feeReceiver).transfer(msg.value);

// Create passport if it does not exist
if(passportRegistry.idPassport(talentId) == address(0)) {
passportRegistry.adminCreate("talent_builder_score", wallet, talentId);
}

// Emit event
require(passportBuilderScore.setScore(talentId, score), "Failed to set score");
emit BuilderScoreSet(wallet, score, talentId);
}
}
42 changes: 0 additions & 42 deletions scripts/passport/deployScorer.ts

This file was deleted.

63 changes: 63 additions & 0 deletions scripts/passport/deployTalentBuilderScore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { ethers, network } from "hardhat";

import { deployTalentBuilderScore } from "../shared";
import { PassportBuilderScore, PassportRegistry } from "../../test/shared/artifacts";

const PASSPORT_REGISTRY_ADDRESS_MAINNET = "0xb477A9BD2547ad61f4Ac22113172Dd909E5B2331";
const PASSPORT_REGISTRY_ADDRESS_TESTNET = "0xa600b3356c1440B6D6e57b0B7862dC3dFB66bc43";

const PASSPORT_BUILDER_SCORE_MAINNET = "0xBBFeDA7c4d8d9Df752542b03CdD715F790B32D0B"
const PASSPORT_BUILDER_SCORE_TESTNET = "0x5f3aA689C4DCBAe505E6F6c8548DbD9b908bA71d"

const FEE_RECEIVER_MAINNET = "0xC925bD0E839E8e22A7DDEbe7f4C21b187deeC358";
const FEE_RECEIVER_TESTNET = "0x08BC8a92e5C99755C675A21BC4FcfFb59E0A9508";

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

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

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

const smartBuilderScore = await deployTalentBuilderScore(
admin.address,
PASSPORT_BUILDER_SCORE_TESTNET,
PASSPORT_REGISTRY_ADDRESS_TESTNET,
FEE_RECEIVER_TESTNET
);

console.log(`Smart Builder Score Address: ${smartBuilderScore.address}`);

console.log("Adding trusted signer");

const passportBuilderScore = new ethers.Contract(
PASSPORT_BUILDER_SCORE_TESTNET,
PassportBuilderScore.abi,
admin
);
await passportBuilderScore.addTrustedSigner(smartBuilderScore.address);

const passportRegistry = new ethers.Contract(
PASSPORT_REGISTRY_ADDRESS_TESTNET,
PassportRegistry.abi,
admin
);

console.log("Transfering ownership");

// Set smart builder score as the owner of passportRegistry so it's the only contract that can create new passports onchain
await passportRegistry.transferOwnership(smartBuilderScore.address);

const newOwner = await passportRegistry.owner();

console.log(`New owner: ${newOwner}`);

console.log("Done");
}

main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
59 changes: 59 additions & 0 deletions scripts/passport/transferPassportBuilderScoreOwnership.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import hre from "hardhat";

import { getContract } from "viem";
import { baseSepolia, base } from "viem/chains";
import { privateKeyToSimpleSmartAccount } from "permissionless/accounts";

import dotenv from "dotenv";

dotenv.config();

import * as PassportBuilderScore from "../../artifacts/contracts/passport/PassportBuilderScore.sol/PassportBuilderScore.json";

const ENTRYPOINT = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
const PASSPORT_BUILDER_SCORE_ADDRESS = "0xBBFeDA7c4d8d9Df752542b03CdD715F790B32D0B"

// Script to transfer ownership of passport buider score to a smart wallet
async function main() {
const [admin] = await hre.viem.getWalletClients();
const publicClient = await hre.viem.getPublicClient();

if(!process.env.PRIVATE_KEY){
console.error("Missing PK");
return
}
const privateKey = `0x${process.env.PRIVATE_KEY}`;

console.log("privateKey", privateKey);
const smartAccount = await privateKeyToSimpleSmartAccount(publicClient, {
privateKey,
entryPoint: ENTRYPOINT, // global entrypoint
factoryAddress: "0x9406Cc6185a346906296840746125a0E44976454",
});

console.log(`Owner SCA ${smartAccount.address}`);

const passportBuilderScore = getContract({
address: PASSPORT_BUILDER_SCORE_ADDRESS,
abi: PassportBuilderScore.abi,
client: {
public: publicClient,
wallet: admin,
},
});

const tx = await passportBuilderScore.write.transferOwnership([smartAccount.address]);

await publicClient.waitForTransactionReceipt({ hash: tx });

const owner = await passportBuilderScore.read.owner();

console.log(`New owner: ${owner}`);
}

main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
16 changes: 8 additions & 8 deletions scripts/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type {
PassportBuilderScore,
TalentCommunitySale,
TalentTGEUnlock,
SmartBuilderScore,
TalentBuilderScore,
PassportWalletRegistry,
TalentTGEUnlockTimestamp,
TalentVault,
Expand Down Expand Up @@ -74,23 +74,23 @@ export async function deployPassportBuilderScore(registry: string, owner: string
return deployedPassportBuilderScore as PassportBuilderScore;
}

export async function deploySmartBuilderScore(
export async function deployTalentBuilderScore(
owner: string,
passportBuilderScore: string,
passportRegistry: string,
feeReceiver: string,
passportBuilderScore: string
): Promise<SmartBuilderScore> {
const smartBuilderScoreContract = await ethers.getContractFactory("SmartBuilderScore");
): Promise<TalentBuilderScore> {
const talentBuilderScoreContract = await ethers.getContractFactory("TalentBuilderScore");

const deployedSmartBuilderScore = await smartBuilderScoreContract.deploy(
const deployedTalentBuilderScore = await talentBuilderScoreContract.deploy(
owner,
passportBuilderScore,
passportRegistry,
feeReceiver
);
await deployedSmartBuilderScore.deployed();
await deployedTalentBuilderScore.deployed();

return deployedSmartBuilderScore as SmartBuilderScore;
return deployedTalentBuilderScore as TalentBuilderScore;
}

export async function deployTalentCommunitySale(
Expand Down
Loading
Loading