Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"lib/predicate-contracts-v2/src/examples/inheritance/MetaCoin.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {PredicateClient} from "../../mixins/PredicateClient.sol";
import {Attestation} from "../../interfaces/IPredicateRegistry.sol";
contract MetaCoin is PredicateClient, Ownable {
mapping(address => uint256) public balances;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
constructor(address _owner, address _registry, string memory _policyID) Ownable(_owner) {
balances[_owner] = 10_000_000_000_000;
_initPredicateClient(_registry, _policyID);
}
function sendCoin(address _receiver, uint256 _amount, Attestation calldata _attestation) external payable {
bytes memory encodedSigAndArgs = abi.encodeWithSignature("_sendCoin(address,uint256)", _receiver, _amount);
require(
_authorizeTransaction(_attestation, encodedSigAndArgs, msg.sender, msg.value),
"MetaCoin: unauthorized transaction"
);
// business logic function that is protected
_sendCoin(_receiver, _amount);
}
function setPolicyID(
string memory _policyID
) external onlyOwner {
_setPolicyID(_policyID);
}
function setRegistry(
address _registry
) public onlyOwner {
_setRegistry(_registry);
}
function _sendCoin(address _receiver, uint256 _amount) internal {
require(balances[msg.sender] >= _amount, "MetaCoin: insufficient balance");
balances[msg.sender] -= _amount;
balances[_receiver] += _amount;
emit Transfer(msg.sender, _receiver, _amount);
}
function getBalance(
address _addr
) public view returns (uint256) {
return balances[_addr];
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/access/Ownable.sol": {
"content": "// 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);
}
}
"
},
"lib/predicate-contracts-v2/src/mixins/PredicateClient.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
import {IPredicateRegistry, Attestation, Statement} from "../interfaces/IPredicateRegistry.sol";
import "../interfaces/IPredicateClient.sol";
/**
* @title PredicateClient
* @author Predicate Labs, Inc (https://predicate.io)
* @notice Abstract contract for integrating Predicate attestation validation
* @dev Provides core functionality for contracts to validate attestations before executing transactions.
* Implements ERC-7201 namespaced storage to prevent collisions in upgradeable contracts.
*
* Usage:
* 1. Inherit this contract
* 2. Call _initPredicateClient() in your constructor
* 3. Use _authorizeTransaction() to validate attestations before business logic
*
* Example:
* ```solidity
* contract MyContract is PredicateClient {
* constructor(address _registry, string memory _policy) {
* _initPredicateClient(_registry, _policy);
* }
*
* function protectedFunction(Attestation calldata _attestation) external {
* bytes memory encoded = abi.encodeWithSignature("_internal()");
* require(_authorizeTransaction(_attestation, encoded, msg.sender, msg.value));
* _internal();
* }
* }
* ```
*/
abstract contract PredicateClient is IPredicateClient {
/// @notice Struct to contain stateful values for PredicateClient-type contracts
/// @custom:storage-location erc7201:predicate.storage.PredicateClient
struct PredicateClientStorage {
IPredicateRegistry registry;
string policy;
}
/// @notice the storage slot for the PredicateClientStorage struct
/// @dev keccak256(abi.encode(uint256(keccak256("predicate.storage.PredicateClient")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant _PREDICATE_CLIENT_STORAGE_SLOT =
0x804776a84f3d03ad8442127b1451e2fbbb6a715c681d6a83c9e9fca787b99300;
/// @notice Emitted when the PredicateRegistry address is updated
event PredicateRegistryUpdated(address indexed oldRegistry, address indexed newRegistry);
/// @notice Emitted when the policy ID is updated
event PredicatePolicyIDUpdated(string oldPolicyID, string newPolicyID);
function _getPredicateClientStorage() private pure returns (PredicateClientStorage storage $) {
assembly {
$.slot := _PREDICATE_CLIENT_STORAGE_SLOT
}
}
/**
* @notice Initializes the Predicate client with registry and policy ID
* @dev Must be called in the constructor of the inheriting contract.
* Sets both the registry address and initial policy ID.
* @param _registryAddress The address of the PredicateRegistry contract
* @param _policyID The initial policy identifier for this contract (typically "x-{hash[:16]}")
*/
function _initPredicateClient(address _registryAddress, string memory _policyID) internal {
PredicateClientStorage storage $ = _getPredicateClientStorage();
$.registry = IPredicateRegistry(_registryAddress);
_setPolicyID(_policyID);
}
/**
* @notice Updates the policy ID for this contract
* @dev Updates local storage and registers with PredicateRegistry.
* Should typically be restricted to owner/admin.
* Emits PredicatePolicyIDUpdated event.
* @param _policyID The new policy identifier to set
*/
function _setPolicyID(
string memory _policyID
) internal {
PredicateClientStorage storage $ = _getPredicateClientStorage();
string memory oldPolicyID = $.policy;
$.policy = _policyID;
$.registry.setPolicyID(_policyID);
emit PredicatePolicyIDUpdated(oldPolicyID, _policyID);
}
function getPolicyID() external view returns (string memory policyID) {
return _getPolicyID();
}
function _getPolicyID() internal view returns (string memory policyID) {
return _getPredicateClientStorage().policy;
}
/**
* @notice Updates the PredicateRegistry address
* @dev Should typically be restricted to owner/admin for security.
* Emits PredicateRegistryUpdated event.
* @param _registryAddress The new PredicateRegistry contract address
* @custom:security Changing registry is sensitive - ensure proper access control
*/
function _setRegistry(
address _registryAddress
) internal {
PredicateClientStorage storage $ = _getPredicateClientStorage();
address oldRegistry = address($.registry);
$.registry = IPredicateRegistry(_registryAddress);
emit PredicateRegistryUpdated(oldRegistry, _registryAddress);
}
function getRegistry() external view returns (address) {
return _getRegistry();
}
function _getRegistry() internal view returns (address) {
return address(_getPredicateClientStorage().registry);
}
modifier onlyPredicateRegistry() {
if (msg.sender != address(_getPredicateClientStorage().registry)) {
revert PredicateClient__Unauthorized();
}
_;
}
/**
* @notice Validates a transaction by verifying the attestation
* @dev Constructs a Statement from parameters and validates it against the attestation.
* This is the core authorization function that should be called before executing
* any protected business logic.
*
* Process:
* 1. Builds Statement struct with transaction parameters
* 2. Calls PredicateRegistry.validateAttestation()
* 3. Registry verifies signature and checks attestation validity
* 4. Returns true if valid (reverts if invalid)
*
* @param _attestation The attestation containing UUID, expiration, attester, and signature
* @param _encodedSigAndArgs The encoded function signature and arguments (use abi.encodeWithSignature)
* @param _msgSender The original transaction sender (typically msg.sender)
* @param _msgValue The ETH value sent with the transaction (typically msg.value)
* @return success Always returns true (reverts on validation failure)
*
* @custom:security Always use this before executing protected functions
* @custom:security Encode the internal function call, not the public one
*/
function _authorizeTransaction(
Attestation memory _attestation,
bytes memory _encodedSigAndArgs,
address _msgSender,
uint256 _msgValue
) internal returns (bool success) {
PredicateClientStorage storage $ = _getPredicateClientStorage();
Statement memory statement = Statement({
msgSender: _msgSender,
target: address(this),
msgValue: _msgValue,
encodedSigAndArgs: _encodedSigAndArgs,
policy: $.policy,
expiration: _attestation.expiration,
uuid: _attestation.uuid
});
return $.registry.validateAttestation(statement, _attestation);
}
}
"
},
"lib/predicate-contracts-v2/src/interfaces/IPredicateRegistry.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
/**
* @notice Struct that bundles together a statement's parameters for validation
* @dev A statement represents a claim or assertion about a transaction to be executed.
* It contains all the necessary information to validate that a transaction
* is authorized by an attester according to a specific policy.
* @custom:security UUID must be unique per statement to prevent replay attacks
*/
struct Statement {
// the unique identifier for the statement
string uuid;
// the address of the sender of the statement
address msgSender;
// the address of the target contract for the statement
address target;
// the value to send with the statement
uint256 msgValue;
// the encoded signature and arguments for the statement
bytes encodedSigAndArgs;
// the policy associated with the statement
string policy;
// the timestamp by which the statement must be executed
uint256 expiration;
}
/**
* @notice Struct that bundles together an attestation's parameters for validation
* @dev An attestation is a signed approval from an authorized attester.
* The signature is created by signing the hash of the corresponding Statement.
* @custom:security signature must be generated using hashStatementWithExpiry()
*/
struct Attestation {
// the unique identifier for the attestation
string uuid;
// the timestamp by which the attestation must be executed
uint256 expiration;
// the address of the attester
address attester;
// the signature from the attestation
bytes signature;
}
/**
* @title IPredicateRegistry
* @author Predicate Labs, Inc (https://predicate.io)
* @notice Interface for the core registry managing attesters, policies, and statement validation
* @dev Defines the contract interface for PredicateRegistry implementation
*/
interface IPredicateRegistry {
/**
* @notice Sets a policy ID for the sender, defining execution rules for statements
* @dev Associates a policy identifier with msg.sender. Policy ID format:
* - Typically: "x-{hash(policy)[:16]}" (e.g., "x-a1b2c3d4e5f6g7h8")
* - Can be any string: IPFS CID, URL, or custom identifier
* - No format validation - any string accepted
* @param policyID The unique identifier for the policy
*/
function setPolicyID(
string memory policyID
) external;
/**
* @notice Retrieves the policy ID associated with a client address
* @param client The address to query
* @return policyID The policy identifier, empty string if none set
*/
function getPolicyID(
address client
) external view returns (string memory policyID);
/**
* @notice Validates an attestation to authorize a statement execution
* @dev Verifies:
* - Attestation not expired
* - Statement UUID not previously used (replay protection)
* - UUIDs match between statement and attestation
* - Expirations match
* - Signature is valid (ECDSA recovery)
* - Attester is registered
* @param _statement The statement to validate
* @param _attestation The signed attestation authorizing the statement
* @return isVerified True if valid, reverts otherwise
* @custom:security Marks UUID as spent to prevent replay attacks
*/
function validateAttestation(
Statement memory _statement,
Attestation memory _attestation
) external returns (bool isVerified);
}
"
},
"lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/utils/Context.sol": {
"content": "// 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;
}
}
"
},
"lib/predicate-contracts-v2/src/interfaces/IPredicateClient.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;
/**
* @notice error type for unauthorized access
*/
error PredicateClient__Unauthorized();
/**
* @notice Interface for a PredicateClient-type contract to set policy, registry and validate tasks
*/
interface IPredicateClient {
/**
* @notice Sets a policy ID for the calling address
* @param _policyID The policy identifier. Typically "x-{hash(policy)[:16]}" but can be any string
* @dev This function enables clients to define execution rules or parameters for statements they submit.
* The policy ID governs how statements are validated, ensuring compliance with predefined rules.
*/
function setPolicyID(
string memory _policyID
) external;
/**
* @notice Sets the Predicate Registry for the calling address
* @param _registry address of the registry
* @dev This function enables clients to set the Predicate Registry for the calling address
* @dev Authorized only by the owner of the contract
*/
function setRegistry(
address _registry
) external;
/**
* @notice Retrieves the policy ID for the calling address
* @return policyID The policy identifier associated with the calling address
*/
function getPolicyID() external view returns (string memory policyID);
/**
* @notice Function for getting the Predicate Registry
* @return address of the registry
*/
function getRegistry() external view returns (address);
}
"
}
},
"settings": {
"remappings": [
"@predicate-contracts-v2/=lib/predicate-contracts-v2/",
"@predicate-contracts/=lib/predicate-contracts/",
"forge-std/=lib/forge-std/src/",
"foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin-upgrades-v4.9.0/=lib/predicate-contracts/lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/",
"@openzeppelin-upgrades/=lib/predicate-contracts/lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable/",
"@openzeppelin-v4.9.0/=lib/predicate-contracts/lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/",
"ds-test/=lib/predicate-contracts/lib/forge-std/lib/ds-test/src/",
"eigenlayer-contracts/=lib/predicate-contracts/lib/eigenlayer-contracts/",
"eigenlayer-middleware/=lib/predicate-contracts/lib/eigenlayer-middleware/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"openzeppelin-contracts-upgradeable-v4.9.0/=lib/predicate-contracts/lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts-v4.9.0/=lib/predicate-contracts/lib/eigenlayer-contracts/lib/openzeppelin-contracts-v4.9.0/",
"openzeppelin-contracts/=lib/predicate-contracts/lib/openzeppelin-contracts/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"openzeppelin-upgradeable/=lib/predicate-contracts/lib/openzeppelin-contracts-upgradeable/contracts/",
"openzeppelin/=lib/predicate-contracts/lib/eigenlayer-contracts/lib/openzeppelin-contracts-upgradeable-v4.9.0/contracts/",
"predicate-contracts-v2/=lib/predicate-contracts-v2/src/",
"predicate-contracts/=lib/predicate-contracts/src/",
"solmate/=lib/predicate-contracts/lib/solmate/src/",
"utils/=lib/predicate-contracts/lib/utils/"
],
"optimizer": {
"enabled": false,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": false
}
}}
Submitted on: 2025-10-17 09:49:03
Comments
Log in to comment.
No comments yet.