ETH Price: $2,868.59 (-2.47%)
 

More Info

Private Name Tags

Multichain Info

1 address found via
Transaction Hash
Block
From
To
Set Disabled383512542025-11-18 18:50:5568 days ago1763491855IN
0x16B5f16F...A15dDbCfD
0 ETH0.000000220.009529
Add Score383510692025-11-18 18:44:4568 days ago1763491485IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000130.00170135
Add Score383484622025-11-18 17:17:5168 days ago1763486271IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000240.00305601
Add Score383369502025-11-18 10:54:0768 days ago1763463247IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.00000010.00139726
Add Score383346972025-11-18 9:39:0168 days ago1763458741IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000120.00053502
Add Score383325632025-11-18 8:27:5368 days ago1763454473IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000510.00549014
Add Score383317572025-11-18 8:01:0168 days ago1763452861IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000320.00393083
Add Score383304192025-11-18 7:16:2568 days ago1763450185IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000620.0040382
Add Score383185552025-11-18 0:40:5769 days ago1763426457IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000170.00222692
Add Score383180452025-11-18 0:23:5769 days ago1763425437IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000080.00113264
Add Score383177342025-11-18 0:13:3569 days ago1763424815IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000340.00438946
Add Score383156352025-11-17 23:03:3769 days ago1763420617IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000110.00150383
Add Score383120132025-11-17 21:02:5369 days ago1763413373IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000250.00315772
Add Score383105252025-11-17 20:13:1769 days ago1763410397IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000002550.03259349
Add Score383070742025-11-17 18:18:1569 days ago1763403495IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.00000190.02531687
Add Score383050702025-11-17 17:11:2769 days ago1763399487IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000360.00325837
Add Score383048082025-11-17 17:02:4369 days ago1763398963IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000850.00345115
Add Score383039772025-11-17 16:35:0169 days ago1763397301IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.00000110.00975231
Add Score382941582025-11-17 11:07:4369 days ago1763377663IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000420.00174413
Add Score382936632025-11-17 10:51:1369 days ago1763376673IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.00000040.00164179
Add Score382910302025-11-17 9:23:2769 days ago1763371407IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000110.00147207
Add Score382899512025-11-17 8:47:2969 days ago1763369249IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000160.00209151
Add Score382837752025-11-17 5:21:3769 days ago1763356897IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.00000010.0013732
Add Score382702662025-11-16 21:51:1970 days ago1763329879IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000390.00355953
Add Score382702282025-11-16 21:50:0370 days ago1763329803IN
0x16B5f16F...A15dDbCfD
0.001 ETH0.000000250.00329735

Latest 25 internal transactions (View All)

Parent Transaction Hash Block From To
383510692025-11-18 18:44:4568 days ago1763491485
0x16B5f16F...A15dDbCfD
0.001 ETH
383484622025-11-18 17:17:5168 days ago1763486271
0x16B5f16F...A15dDbCfD
0.001 ETH
383369502025-11-18 10:54:0768 days ago1763463247
0x16B5f16F...A15dDbCfD
0.001 ETH
383346972025-11-18 9:39:0168 days ago1763458741
0x16B5f16F...A15dDbCfD
0.001 ETH
383325632025-11-18 8:27:5368 days ago1763454473
0x16B5f16F...A15dDbCfD
0.001 ETH
383317572025-11-18 8:01:0168 days ago1763452861
0x16B5f16F...A15dDbCfD
0.001 ETH
383304192025-11-18 7:16:2568 days ago1763450185
0x16B5f16F...A15dDbCfD
0.001 ETH
383185552025-11-18 0:40:5769 days ago1763426457
0x16B5f16F...A15dDbCfD
0.001 ETH
383180452025-11-18 0:23:5769 days ago1763425437
0x16B5f16F...A15dDbCfD
0.001 ETH
383177342025-11-18 0:13:3569 days ago1763424815
0x16B5f16F...A15dDbCfD
0.001 ETH
383156352025-11-17 23:03:3769 days ago1763420617
0x16B5f16F...A15dDbCfD
0.001 ETH
383120132025-11-17 21:02:5369 days ago1763413373
0x16B5f16F...A15dDbCfD
0.001 ETH
383105252025-11-17 20:13:1769 days ago1763410397
0x16B5f16F...A15dDbCfD
0.001 ETH
383070742025-11-17 18:18:1569 days ago1763403495
0x16B5f16F...A15dDbCfD
0.001 ETH
383050702025-11-17 17:11:2769 days ago1763399487
0x16B5f16F...A15dDbCfD
0.001 ETH
383048082025-11-17 17:02:4369 days ago1763398963
0x16B5f16F...A15dDbCfD
0.001 ETH
383039772025-11-17 16:35:0169 days ago1763397301
0x16B5f16F...A15dDbCfD
0.001 ETH
382941582025-11-17 11:07:4369 days ago1763377663
0x16B5f16F...A15dDbCfD
0.001 ETH
382936632025-11-17 10:51:1369 days ago1763376673
0x16B5f16F...A15dDbCfD
0.001 ETH
382910302025-11-17 9:23:2769 days ago1763371407
0x16B5f16F...A15dDbCfD
0.001 ETH
382899512025-11-17 8:47:2969 days ago1763369249
0x16B5f16F...A15dDbCfD
0.001 ETH
382837752025-11-17 5:21:3769 days ago1763356897
0x16B5f16F...A15dDbCfD
0.001 ETH
382702662025-11-16 21:51:1970 days ago1763329879
0x16B5f16F...A15dDbCfD
0.001 ETH
382702282025-11-16 21:50:0370 days ago1763329803
0x16B5f16F...A15dDbCfD
0.001 ETH
382672072025-11-16 20:09:2170 days ago1763323761
0x16B5f16F...A15dDbCfD
0.001 ETH
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
TalentBuilderScore

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 2000 runs

Other Settings:
paris EvmVersion
// 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);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS
    }

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
     * return address(0) without also returning an error description. Errors are documented using an enum (error type)
     * and a bytes32 providing additional information about the error.
     *
     * If no error is returned, then the address can be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError, bytes32) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS, s);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature, bytes32(0));
        }

        return (signer, RecoverError.NoError, bytes32(0));
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)

pragma solidity ^0.8.20;

import {Strings} from "../Strings.sol";

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    bool private _paused;

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

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

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/access/Ownable.sol";
import "./PassportRegistry.sol";

contract PassportBuilderScore is Ownable {
    PassportRegistry public passportRegistry;

    // Mapping to store scores for each passport ID
    mapping(uint256 => uint256) private passportScores;

    // Mapping to store timestamps of last updates for each passport ID
    mapping(uint256 => uint256) private passportLastUpdate;

    // Mapping to store trusted signers
    mapping(address => bool) public trustedSigners;

    event ScoreUpdated(uint256 indexed passportId, uint256 score, uint256 timestamp);
    event PassportRegistryChanged(address indexed oldAddress, address indexed newAddress);

    uint256 public EXPIRATION_TIME = 1 days * 90; // 90 days

    constructor(address passportRegistryAddress, address initialOwner) Ownable(initialOwner) {
        passportRegistry = PassportRegistry(passportRegistryAddress);
        trustedSigners[initialOwner] = true;
    }

    /**
     * @notice Sets the expiration time for the scores.
     * @dev Can only be called by the owner.
     * @param newExpirationTime The new expiration time in days.
     */
    function setExpirationTime(uint256 newExpirationTime) external onlyOwner {
        EXPIRATION_TIME = 1 days * newExpirationTime;
    }

    /**
     * @notice Adds the given address to the list of trusted signers.
     * @dev Can only be called by the owner.
     * @param signer The address to add to the list of trusted signers.
     */
    function addTrustedSigner(address signer) external onlyOwner {
        trustedSigners[signer] = true;
    }

    /**
     * @notice Removes the given address from the list of trusted signers.
     * @dev Can only be called by the owner.
     * @param signer The address to remove from the list of trusted signers.
     */
    function removeTrustedSigner(address signer) external onlyOwner {
        trustedSigners[signer] = false;
    }

    /**
     * @notice Sets the score for a given passport ID.
     * @dev Can only be called by the owner.
     * @param passportId The ID of the passport to set the score for.
     * @param score The score to set for the passport ID.
     */
    function setScore(uint256 passportId, uint256 score) external returns (bool) {
        require(trustedSigners[msg.sender], "Caller is not a trusted signer");
        require(passportRegistry.idPassport(passportId) != address(0), "Passport ID does not exist");
        passportScores[passportId] = score;
        passportLastUpdate[passportId] = block.timestamp;
        emit ScoreUpdated(passportId, score, block.timestamp);
        return true;
    }

    /**
     * @notice Gets the score of a given passport ID.
     * @param passportId The ID of the passport to get the score for.
     * @return The score of the given passport ID.
     */
    function getScore(uint256 passportId) public view returns (uint256) {
        uint256 lastUpdate = passportLastUpdate[passportId] == 0 ? block.timestamp : passportLastUpdate[passportId];
        require(lastUpdate + EXPIRATION_TIME >= block.timestamp, "Score is expired");
        return passportScores[passportId];
    }

    /**
     * @notice Gets the timestamp of the last update for a given passport ID.
     * @param passportId The ID of the passport to get the last update timestamp for.
     * @return The timestamp of the last update for the given passport ID.
     */
    function getLastUpdate(uint256 passportId) external view returns (uint256) {
        return passportLastUpdate[passportId];
    }

    function getLastUpdateByAddress(address wallet) external view returns (uint256) {
        return passportLastUpdate[passportRegistry.passportId(wallet)];
    }

    /**
     * @notice Gets the score of a given address.
     * @param wallet The address to get the score for.
     * @return The score of the given address.
     */
    function getScoreByAddress(address wallet) external view returns (uint256) {
        uint256 passportId = passportRegistry.passportId(wallet);
        require(passportRegistry.idPassport(passportId) != address(0), "Passport ID does not exist");

        uint256 score = getScore(passportId);
        return score;
    }

    /**
     * @notice Changes the address of the PassportRegistry contract.
     * @dev Can only be called by the owner.
     * @param newPassportRegistryAddress The address of the new PassportRegistry contract.
     */
    function setPassportRegistry(address newPassportRegistryAddress) external onlyOwner {
        require(newPassportRegistryAddress != address(0), "Invalid address");
        address oldAddress = address(passportRegistry);
        passportRegistry = PassportRegistry(newPassportRegistryAddress);
        emit PassportRegistryChanged(oldAddress, newPassportRegistryAddress);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract PassportRegistry is Ownable, Pausable {
    // 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
    uint256 public totalCreates;

    // Total number of passports sequencially created
    uint256 public totalSequencialCreates;

    // Total number of passports created by admins
    uint256 public totalAdminsCreates;

    // Total number of passport transfers
    uint256 public totalPassportTransfers;

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

    // 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);

    // Transfer request initiated
    event TransferRequested(address indexed fromWallet, address indexed toWallet, uint256 passportId);

    // Transfer request accepted
    event TransferAccepted(address indexed fromWallet, address indexed toWallet, uint256 passportId);

    // Transfer request revoked
    event TransferRevoked(address indexed wallet, uint256 passportId);

    mapping(uint256 => address) public transferRequests;

    /**
     * @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 initialOwner) Ownable(initialOwner) {
        _sequencial = false;
    }

    /**
     * @notice Creates a new passport with the next sequential ID.
     * @dev Can only be called when the contract is in sequential generation mode and not paused.
     * @param source The source of the passport creation.
     */
    function create(string memory source) public whenNotPaused whenSequencialGeneration {
        require(passportId[msg.sender] == 0, "Passport already exists");

        totalSequencialCreates++;

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

    /**
     * @notice Creates a new passport with a specified ID for a specific wallet.
     * @dev Can only be called by the owner when the contract is in admin generation mode and not paused.
     * @param source The source of the passport creation.
     * @param wallet The address of the wallet to associate with the new passport.
     * @param id The ID to assign to the new passport.
     */
    function adminCreate(
        string memory source,
        address wallet,
        uint256 id
    ) public onlyOwner whenNotPaused whenAdminGeneration {
        require(passportId[wallet] == 0, "Passport already exists");

        totalAdminsCreates++;

        _create(wallet, id, source);
    }

    /**
     * @notice Transfers the passport ID of the msg.sender to the new wallet.
     * @dev Can only be called by the passport owner and when the contract is not paused.
     * @param newWallet The address of the new wallet to transfer the passport to.
     */
    function transfer(address newWallet) public whenNotPaused {
        uint256 id = passportId[msg.sender];
        require(newWallet != msg.sender, "You can not transfer to yourself");
        require(newWallet != address(0), "You can not transfer to zero address");
        require(id != 0, "Passport does not exist");
        require(passportId[newWallet] == 0, "Wallet passed already has a passport");
        require(transferRequests[id] == address(0), "Pending transfer already exists for this passport ID");

        transferRequests[id] = newWallet;

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

    /**
     * @notice Accepts a pending passport transfer to the msg.sender's wallet.
     * @dev Can be called by the new wallet to accept the transfer.
     */
    function acceptTransfer(uint256 _passportId) public whenNotPaused {
        address newWallet = transferRequests[_passportId];
        require(newWallet == msg.sender, "You are not authorized to accept this transfer");

        address oldWallet = idPassport[_passportId];
        require(oldWallet != address(0), "Passport does not exist");

        passportId[oldWallet] = 0;
        passportId[newWallet] = _passportId;
        idPassport[_passportId] = newWallet;
        walletActive[oldWallet] = false;
        walletActive[newWallet] = true;
        totalPassportTransfers++;

        delete transferRequests[_passportId];

        emit TransferAccepted(oldWallet, newWallet, _passportId);
        emit Transfer(_passportId, _passportId, oldWallet, newWallet);
    }

    /**
     * @notice Revokes a pending passport transfer.
     * @dev Can only be called by the passport owner and when the contract is not paused.
     * @param _passportId The ID of the passport for which to revoke the transfer.
     */
    function revokeTransfer(uint256 _passportId) public whenNotPaused {
        address owner = idPassport[_passportId];
        require(owner == msg.sender, "You are not the owner of this passport");
        require(transferRequests[_passportId] != address(0), "No pending transfer to revoke");

        delete transferRequests[_passportId];

        emit TransferRevoked(msg.sender, _passportId);
    }

    // Admin

    /**
     * @notice Transfers the passport ID from one wallet to another.
     * @dev Can only be called by the owner (aka admin).
     * @param wallet The address of the wallet to transfer the passport from.
     * @param id The new passport ID to assign to the wallet.
     */
    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++;

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

    /**
     * @notice Activates the passport with the given passport ID.
     * @dev Can only be called by the owner when the contract is not paused.
     * @param _passportId The ID of the passport to activate.
     */
    function activate(uint256 _passportId) public whenNotPaused onlyOwner {
        address wallet = idPassport[_passportId];
        require(wallet != address(0), "Passport must exist");
        require(walletActive[wallet] == false, "Passport must be inactive");

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

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

    /**
     * @notice Deactivates the passport with the given passport ID.
     * @dev Can only be called by the owner when the contract is not paused.
     * @param _passportId The ID of the passport to deactivate.
     */
    function deactivate(uint256 _passportId) public whenNotPaused onlyOwner {
        address wallet = idPassport[_passportId];
        require(wallet != address(0), "Passport must exist");
        require(walletActive[wallet] == true, "Passport must be active");

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

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

    /**
     * @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.
     * @param sequentialFlag Set to true for sequential generation mode, false for admin generation mode.
     * @param nextSequentialPassportId The next sequential passport ID to be issued.
     */
    function setGenerationMode(bool sequentialFlag, uint256 nextSequentialPassportId) public onlyOwner {
        _sequencial = sequentialFlag;
        _nextSequentialPassportId = nextSequentialPassportId;

        emit PassportGenerationChanged(sequentialFlag, nextSequentialPassportId);
    }

    /**
     * @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 _nextSequentialPassportId;
    }

    // private

    /**
     * @dev Creates a new passport with the given ID for the specified wallet.
     * @param wallet The address of the wallet to associate with the new passport.
     * @param id The ID to assign to the new passport.
     * @param source The source of the passport creation.
     */
    function _create(address wallet, uint256 id, string memory source) private {
        require(idPassport[id] == address(0), "Passport id already issued");

        totalCreates++;

        idPassport[id] = wallet;
        passportId[wallet] = id;
        walletActive[wallet] = true;
        idActive[id] = true;
        idSource[id] = source;
        
        uint256 result = sourcePassports[source] + 1;
        sourcePassports[source] = result;

        emit Create(wallet, id, source);
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 2000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_trustedSigner","type":"address"},{"internalType":"address","name":"_passportBuilderScoreAddress","type":"address"},{"internalType":"address","name":"_passportRegistryAddress","type":"address"},{"internalType":"address","name":"_feeReceiver","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"score","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"talentId","type":"uint256"}],"name":"BuilderScoreSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"uint256","name":"score","type":"uint256"},{"internalType":"uint256","name":"talentId","type":"uint256"},{"internalType":"address","name":"wallet","type":"address"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"addScore","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"cost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"enabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"passportBuilderScore","outputs":[{"internalType":"contract PassportBuilderScore","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"passportRegistry","outputs":[{"internalType":"contract PassportRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_cost","type":"uint256"}],"name":"setCost","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setDisabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"setEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"setPassportRegistryOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"trustedSigner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_feeReceiver","type":"address"}],"name":"updateReceiver","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6080604052655af3107a400060055534801561001a57600080fd5b5060405162000f6d38038062000f6d83398101604081905261003b9161013f565b836001600160a01b03811661006a57604051631e4fbdf760e01b81526000600482015260240160405180910390fd5b610073816100d3565b50600180546001600160a01b03199081166001600160a01b0396871617825560038054821695871695909517909455600480548516938616939093179092556002805490931693169290921790556006805460ff19169091179055610193565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b038116811461013a57600080fd5b919050565b6000806000806080858703121561015557600080fd5b61015e85610123565b935061016c60208601610123565b925061017a60408601610123565b915061018860608601610123565b905092959194509250565b610dca80620001a36000396000f3fe6080604052600436106100e85760003560e01c80636acb1dcb1161008a5780638ec280cf116100595780638ec280cf14610260578063b3f0067414610275578063f2fde38b14610295578063f74d5480146102b557600080fd5b80636acb1dcb146101fa578063715018a61461020d5780637ba2196a146102225780638da5cb5b1461024257600080fd5b80633526e93e116100c65780633526e93e1461016257806344a0d68a1461019a578063492cad13146101ba57806350b72c01146101da57600080fd5b806313faede6146100ed578063238dafe014610116578063328d8f7214610140575b600080fd5b3480156100f957600080fd5b5061010360055481565b6040519081526020015b60405180910390f35b34801561012257600080fd5b506006546101309060ff1681565b604051901515815260200161010d565b34801561014c57600080fd5b5061016061015b366004610bb6565b6102d5565b005b34801561016e57600080fd5b50600354610182906001600160a01b031681565b6040516001600160a01b03909116815260200161010d565b3480156101a657600080fd5b506101606101b5366004610bda565b6102f0565b3480156101c657600080fd5b50600454610182906001600160a01b031681565b3480156101e657600080fd5b506101606101f5366004610c08565b6102fd565b610160610208366004610c54565b610382565b34801561021957600080fd5b506101606107eb565b34801561022e57600080fd5b5061016061023d366004610c08565b6107ff565b34801561024e57600080fd5b506000546001600160a01b0316610182565b34801561026c57600080fd5b50610160610841565b34801561028157600080fd5b50600254610182906001600160a01b031681565b3480156102a157600080fd5b506101606102b0366004610c08565b610855565b3480156102c157600080fd5b50600154610182906001600160a01b031681565b6102dd6108ac565b6006805460ff1916911515919091179055565b6102f86108ac565b600555565b6103056108ac565b600480546040517ff2fde38b0000000000000000000000000000000000000000000000000000000081526001600160a01b038481169382019390935291169063f2fde38b90602401600060405180830381600087803b15801561036757600080fd5b505af115801561037b573d6000803e3d6000fd5b5050505050565b60065460ff166103ff5760405162461bcd60e51b815260206004820152603760248201527f53657474696e6720746865204275696c6465722053636f72652069732064697360448201527f61626c656420666f72207468697320636f6e747261637400000000000000000060648201526084015b60405180910390fd5b6005543410156104515760405162461bcd60e51b815260206004820152601460248201527f496e73756666696369656e74207061796d656e7400000000000000000000000060448201526064016103f6565b604080516020808201879052818301869052606085811b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169083015282518083036054018152607490920190925280519101207f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c829052603c81206104de90846108f2565b6001549091506001600160a01b0380831691161461053e5760405162461bcd60e51b815260206004820152601160248201527f496e76616c6964207369676e617475726500000000000000000000000000000060448201526064016103f6565b6002546040516001600160a01b03909116903480156108fc02916000818181858888f19350505050158015610577573d6000803e3d6000fd5b50600480546040517f7bdfddd20000000000000000000000000000000000000000000000000000000081529182018790526000916001600160a01b0390911690637bdfddd290602401602060405180830381865afa1580156105dd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106019190610d2b565b6001600160a01b0316036106c457600480546040517fa5a968d7000000000000000000000000000000000000000000000000000000008152606092810192909252601460648301527f74616c656e745f6275696c6465725f73636f726500000000000000000000000060848301526001600160a01b03868116602484015260448301889052169063a5a968d79060a401600060405180830381600087803b1580156106ab57600080fd5b505af11580156106bf573d6000803e3d6000fd5b505050505b6003546040517fb276fa0a00000000000000000000000000000000000000000000000000000000815260048101879052602481018890526001600160a01b039091169063b276fa0a906044016020604051808303816000875af115801561072f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107539190610d48565b61079f5760405162461bcd60e51b815260206004820152601360248201527f4661696c656420746f207365742073636f72650000000000000000000000000060448201526064016103f6565b60408051878152602081018790526001600160a01b038616917f3e7987c229b5c6d85a1ac22fbded798e7f721d6591611ab30f0e42d22e6fcf42910160405180910390a2505050505050565b6107f36108ac565b6107fd600061091c565b565b6108076108ac565b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6108496108ac565b6006805460ff19169055565b61085d6108ac565b6001600160a01b0381166108a0576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024016103f6565b6108a98161091c565b50565b6000546001600160a01b031633146107fd576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016103f6565b6000806000806109028686610984565b92509250925061091282826109d1565b5090949350505050565b600080546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600080600083516041036109be5760208401516040850151606086015160001a6109b088828585610ad9565b9550955095505050506109ca565b50508151600091506002905b9250925092565b60008260038111156109e5576109e5610d65565b036109ee575050565b6001826003811115610a0257610a02610d65565b03610a39576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002826003811115610a4d57610a4d610d65565b03610a87576040517ffce698f7000000000000000000000000000000000000000000000000000000008152600481018290526024016103f6565b6003826003811115610a9b57610a9b610d65565b03610ad5576040517fd78bce0c000000000000000000000000000000000000000000000000000000008152600481018290526024016103f6565b5050565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115610b145750600091506003905082610b9e565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015610b68573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116610b9457506000925060019150829050610b9e565b9250600091508190505b9450945094915050565b80151581146108a957600080fd5b600060208284031215610bc857600080fd5b8135610bd381610ba8565b9392505050565b600060208284031215610bec57600080fd5b5035919050565b6001600160a01b03811681146108a957600080fd5b600060208284031215610c1a57600080fd5b8135610bd381610bf3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060008060808587031215610c6a57600080fd5b84359350602085013592506040850135610c8381610bf3565b9150606085013567ffffffffffffffff80821115610ca057600080fd5b818701915087601f830112610cb457600080fd5b813581811115610cc657610cc6610c25565b604051601f8201601f19908116603f01168101908382118183101715610cee57610cee610c25565b816040528281528a6020848701011115610d0757600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b600060208284031215610d3d57600080fd5b8151610bd381610bf3565b600060208284031215610d5a57600080fd5b8151610bd381610ba8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea26469706673582212206fa49498054a866fba8b6aa5fa6fa7cf4d33df28f99f56dcf2d74cc485aa130464736f6c63430008180033000000000000000000000000b1387ce926d1a2b4c8d90373e462dfeb8873a88d000000000000000000000000bbfeda7c4d8d9df752542b03cdd715f790b32d0b000000000000000000000000b477a9bd2547ad61f4ac22113172dd909e5b2331000000000000000000000000482c953a96769b6d0a1de5777f7405a55f5daec0

Deployed Bytecode

0x6080604052600436106100e85760003560e01c80636acb1dcb1161008a5780638ec280cf116100595780638ec280cf14610260578063b3f0067414610275578063f2fde38b14610295578063f74d5480146102b557600080fd5b80636acb1dcb146101fa578063715018a61461020d5780637ba2196a146102225780638da5cb5b1461024257600080fd5b80633526e93e116100c65780633526e93e1461016257806344a0d68a1461019a578063492cad13146101ba57806350b72c01146101da57600080fd5b806313faede6146100ed578063238dafe014610116578063328d8f7214610140575b600080fd5b3480156100f957600080fd5b5061010360055481565b6040519081526020015b60405180910390f35b34801561012257600080fd5b506006546101309060ff1681565b604051901515815260200161010d565b34801561014c57600080fd5b5061016061015b366004610bb6565b6102d5565b005b34801561016e57600080fd5b50600354610182906001600160a01b031681565b6040516001600160a01b03909116815260200161010d565b3480156101a657600080fd5b506101606101b5366004610bda565b6102f0565b3480156101c657600080fd5b50600454610182906001600160a01b031681565b3480156101e657600080fd5b506101606101f5366004610c08565b6102fd565b610160610208366004610c54565b610382565b34801561021957600080fd5b506101606107eb565b34801561022e57600080fd5b5061016061023d366004610c08565b6107ff565b34801561024e57600080fd5b506000546001600160a01b0316610182565b34801561026c57600080fd5b50610160610841565b34801561028157600080fd5b50600254610182906001600160a01b031681565b3480156102a157600080fd5b506101606102b0366004610c08565b610855565b3480156102c157600080fd5b50600154610182906001600160a01b031681565b6102dd6108ac565b6006805460ff1916911515919091179055565b6102f86108ac565b600555565b6103056108ac565b600480546040517ff2fde38b0000000000000000000000000000000000000000000000000000000081526001600160a01b038481169382019390935291169063f2fde38b90602401600060405180830381600087803b15801561036757600080fd5b505af115801561037b573d6000803e3d6000fd5b5050505050565b60065460ff166103ff5760405162461bcd60e51b815260206004820152603760248201527f53657474696e6720746865204275696c6465722053636f72652069732064697360448201527f61626c656420666f72207468697320636f6e747261637400000000000000000060648201526084015b60405180910390fd5b6005543410156104515760405162461bcd60e51b815260206004820152601460248201527f496e73756666696369656e74207061796d656e7400000000000000000000000060448201526064016103f6565b604080516020808201879052818301869052606085811b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000169083015282518083036054018152607490920190925280519101207f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000908152601c829052603c81206104de90846108f2565b6001549091506001600160a01b0380831691161461053e5760405162461bcd60e51b815260206004820152601160248201527f496e76616c6964207369676e617475726500000000000000000000000000000060448201526064016103f6565b6002546040516001600160a01b03909116903480156108fc02916000818181858888f19350505050158015610577573d6000803e3d6000fd5b50600480546040517f7bdfddd20000000000000000000000000000000000000000000000000000000081529182018790526000916001600160a01b0390911690637bdfddd290602401602060405180830381865afa1580156105dd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106019190610d2b565b6001600160a01b0316036106c457600480546040517fa5a968d7000000000000000000000000000000000000000000000000000000008152606092810192909252601460648301527f74616c656e745f6275696c6465725f73636f726500000000000000000000000060848301526001600160a01b03868116602484015260448301889052169063a5a968d79060a401600060405180830381600087803b1580156106ab57600080fd5b505af11580156106bf573d6000803e3d6000fd5b505050505b6003546040517fb276fa0a00000000000000000000000000000000000000000000000000000000815260048101879052602481018890526001600160a01b039091169063b276fa0a906044016020604051808303816000875af115801561072f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107539190610d48565b61079f5760405162461bcd60e51b815260206004820152601360248201527f4661696c656420746f207365742073636f72650000000000000000000000000060448201526064016103f6565b60408051878152602081018790526001600160a01b038616917f3e7987c229b5c6d85a1ac22fbded798e7f721d6591611ab30f0e42d22e6fcf42910160405180910390a2505050505050565b6107f36108ac565b6107fd600061091c565b565b6108076108ac565b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6108496108ac565b6006805460ff19169055565b61085d6108ac565b6001600160a01b0381166108a0576040517f1e4fbdf7000000000000000000000000000000000000000000000000000000008152600060048201526024016103f6565b6108a98161091c565b50565b6000546001600160a01b031633146107fd576040517f118cdaa70000000000000000000000000000000000000000000000000000000081523360048201526024016103f6565b6000806000806109028686610984565b92509250925061091282826109d1565b5090949350505050565b600080546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600080600083516041036109be5760208401516040850151606086015160001a6109b088828585610ad9565b9550955095505050506109ca565b50508151600091506002905b9250925092565b60008260038111156109e5576109e5610d65565b036109ee575050565b6001826003811115610a0257610a02610d65565b03610a39576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002826003811115610a4d57610a4d610d65565b03610a87576040517ffce698f7000000000000000000000000000000000000000000000000000000008152600481018290526024016103f6565b6003826003811115610a9b57610a9b610d65565b03610ad5576040517fd78bce0c000000000000000000000000000000000000000000000000000000008152600481018290526024016103f6565b5050565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115610b145750600091506003905082610b9e565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015610b68573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116610b9457506000925060019150829050610b9e565b9250600091508190505b9450945094915050565b80151581146108a957600080fd5b600060208284031215610bc857600080fd5b8135610bd381610ba8565b9392505050565b600060208284031215610bec57600080fd5b5035919050565b6001600160a01b03811681146108a957600080fd5b600060208284031215610c1a57600080fd5b8135610bd381610bf3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060008060808587031215610c6a57600080fd5b84359350602085013592506040850135610c8381610bf3565b9150606085013567ffffffffffffffff80821115610ca057600080fd5b818701915087601f830112610cb457600080fd5b813581811115610cc657610cc6610c25565b604051601f8201601f19908116603f01168101908382118183101715610cee57610cee610c25565b816040528281528a6020848701011115610d0757600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b600060208284031215610d3d57600080fd5b8151610bd381610bf3565b600060208284031215610d5a57600080fd5b8151610bd381610ba8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea26469706673582212206fa49498054a866fba8b6aa5fa6fa7cf4d33df28f99f56dcf2d74cc485aa130464736f6c63430008180033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000b1387ce926d1a2b4c8d90373e462dfeb8873a88d000000000000000000000000bbfeda7c4d8d9df752542b03cdd715f790b32d0b000000000000000000000000b477a9bd2547ad61f4ac22113172dd909e5b2331000000000000000000000000482c953a96769b6d0a1de5777f7405a55f5daec0

-----Decoded View---------------
Arg [0] : _trustedSigner (address): 0xb1387cE926d1A2B4C8d90373e462DFeB8873a88D
Arg [1] : _passportBuilderScoreAddress (address): 0xBBFeDA7c4d8d9Df752542b03CdD715F790B32D0B
Arg [2] : _passportRegistryAddress (address): 0xb477A9BD2547ad61f4Ac22113172Dd909E5B2331
Arg [3] : _feeReceiver (address): 0x482C953A96769b6d0A1dE5777f7405A55F5DaEC0

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000b1387ce926d1a2b4c8d90373e462dfeb8873a88d
Arg [1] : 000000000000000000000000bbfeda7c4d8d9df752542b03cdd715f790b32d0b
Arg [2] : 000000000000000000000000b477a9bd2547ad61f4ac22113172dd909e5b2331
Arg [3] : 000000000000000000000000482c953a96769b6d0a1de5777f7405a55f5daec0


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.