/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
// Struct with risk and valuation related information for a certain asset.
struct AssetValueAndRiskFactors {
// The value of the asset.
uint256 assetValue;
// The collateral factor of the asset, for a given creditor.
uint256 collateralFactor;
// The liquidation factor of the asset, for a given creditor.
uint256 liquidationFactor;
}
/**
* @title Asset Valuation Library
* @author Pragma Labs
* @notice The Asset Valuation Library is responsible for calculating the risk weighted values of combinations of assets.
*/
library AssetValuationLib {
/*///////////////////////////////////////////////////////////////
CONSTANTS
///////////////////////////////////////////////////////////////*/
uint256 internal constant ONE_4 = 10_000;
/*///////////////////////////////////////////////////////////////
RISK FACTORS
///////////////////////////////////////////////////////////////*/
/**
* @notice Calculates the collateral value given a combination of asset values and corresponding collateral factors.
* @param valuesAndRiskFactors Array of asset values and corresponding collateral factors.
* @return collateralValue The collateral value of the given assets.
*/
function _calculateCollateralValue(AssetValueAndRiskFactors[] memory valuesAndRiskFactors)
internal
pure
returns (uint256 collateralValue)
{
for (uint256 i; i < valuesAndRiskFactors.length; ++i) {
collateralValue += valuesAndRiskFactors[i].assetValue * valuesAndRiskFactors[i].collateralFactor;
}
collateralValue = collateralValue / ONE_4;
}
/**
* @notice Calculates the liquidation value given a combination of asset values and corresponding liquidation factors.
* @param valuesAndRiskFactors List of asset values and corresponding liquidation factors.
* @return liquidationValue The liquidation value of the given assets.
*/
function _calculateLiquidationValue(AssetValueAndRiskFactors[] memory valuesAndRiskFactors)
internal
pure
returns (uint256 liquidationValue)
{
for (uint256 i; i < valuesAndRiskFactors.length; ++i) {
liquidationValue += valuesAndRiskFactors[i].assetValue * valuesAndRiskFactors[i].liquidationFactor;
}
liquidationValue = liquidationValue / ONE_4;
}
}
/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { GuardianErrors } from "../libraries/Errors.sol";
import { Owned } from "../../lib/solmate/src/auth/Owned.sol";
/**
* @title Guardian
* @author Pragma Labs
* @notice Abstract contract with the minimal implementation of a Guardian contract.
* It implements the following logic:
* - An authorized guardian can trigger an emergency stop.
* - The protocol owner can unpause functionalities one-by-one.
* - Anyone can unpause all functionalities after a fixed cool-down period.
*/
abstract contract BaseGuardian is Owned {
/* //////////////////////////////////////////////////////////////
STORAGE
////////////////////////////////////////////////////////////// */
// Last timestamp an emergency stop was triggered.
uint96 public pauseTimestamp;
// Address of the Guardian.
address public guardian;
/* //////////////////////////////////////////////////////////////
EVENTS
////////////////////////////////////////////////////////////// */
event GuardianChanged(address indexed user, address indexed newGuardian);
/* //////////////////////////////////////////////////////////////
MODIFIERS
////////////////////////////////////////////////////////////// */
/**
* @dev Only guardians can call functions with this modifier.
*/
modifier onlyGuardian() {
if (msg.sender != guardian) revert GuardianErrors.OnlyGuardian();
_;
}
/**
* @dev The public unpause() function, or a second pause() function, can only called a fixed coolDownPeriod after an initial pause().
* This gives the protocol owner time to investigate and solve potential issues,
* but ensures that no rogue owner or guardian can lock user funds for an indefinite amount of time.
*/
modifier afterCoolDownOf(uint256 coolDownPeriod) {
if (block.timestamp <= pauseTimestamp + coolDownPeriod) revert GuardianErrors.CoolDownPeriodNotPassed();
_;
}
/* //////////////////////////////////////////////////////////////
CONSTRUCTOR
////////////////////////////////////////////////////////////// */
constructor() Owned(msg.sender) { }
/* //////////////////////////////////////////////////////////////
GUARDIAN LOGIC
////////////////////////////////////////////////////////////// */
/**
* @notice This function is used to set the guardian address
* @param guardian_ The address of the new guardian.
*/
function changeGuardian(address guardian_) external onlyOwner {
emit GuardianChanged(msg.sender, guardian = guardian_);
}
/* //////////////////////////////////////////////////////////////
PAUSING LOGIC
////////////////////////////////////////////////////////////// */
/**
* @notice This function is used to pause all the flags of the contract.
* @dev The Guardian can only pause the protocol again after 32 days have passed since the last pause.
* This is to prevent that a malicious owner or guardian can take user funds hostage for an indefinite time.
* After the guardian has paused the protocol, the owner has 30 days to find potential problems,
* find a solution and unpause the protocol. If the protocol is not unpaused after 30 days,
* an emergency procedure can be started by any user to unpause the protocol.
* All users have now at least a two-day window to withdraw assets and close positions before
* the protocol can again be paused 32 days after the contract was previously paused.
*/
function pause() external virtual;
/**
* @notice This function is used to unpause flags that could be abused to lock user assets.
* @dev If the protocol is not unpaused after 30 days, any user can unpause the protocol.
* This ensures that no rogue owner or guardian can lock user funds for an indefinite amount of time.
* All users have now at least a two-day window to withdraw assets and close positions before
* the protocol can again be paused 32 days after the contract was previously paused.
*/
function unpause() external virtual;
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
/*//////////////////////////////////////////////////////////////
ERC721 BALANCE/OWNER STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) internal _ownerOf;
mapping(address => uint256) internal _balanceOf;
function ownerOf(uint256 id) public view virtual returns (address owner) {
require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
}
function balanceOf(address owner) public view virtual returns (uint256) {
require(owner != address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
ERC721 APPROVAL STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
/*//////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 id) public virtual {
address owner = _ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == _ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes calldata data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.
unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = _ownerOf[id];
require(owner != address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.
unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
/*//////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}
/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
library AccountErrors {
error AccountInAuction();
error AccountNotLiquidatable();
error AccountUnhealthy();
error AlreadyInitialized();
error CreditorAlreadySet();
error CreditorNotSet();
error CoolDownPeriodNotPassed();
error InvalidAccountVersion();
error InvalidERC20Id();
error InvalidERC721Amount();
error InvalidRecipient();
error InvalidRegistry();
error NoFallback();
error NoReentry();
error NonZeroOpenPosition();
error NumeraireNotFound();
error OnlyFactory();
error OnlyCreditor();
error OnlyLiquidator();
error OnlyOwner();
error TooManyAssets();
error UnknownAsset();
error UnknownAssetType();
}
library FactoryErrors {
error AccountVersionBlocked();
error FactoryMismatch();
error InvalidAccountVersion();
error InvalidRecipient();
error InvalidUpgrade();
error ImplIsZero();
error OnlyAccount();
error OnlyAccountOwner();
error UnsafeRecipient();
error VersionMismatch();
error VersionRootIsZero();
}
library GuardianErrors {
// Thrown when the cool-down period has not yet passed.
error CoolDownPeriodNotPassed();
// Thrown when the functionality is paused.
error FunctionIsPaused();
// Thrown when the caller is not the Guardian.
error OnlyGuardian();
}
library RegistryErrors {
error AssetAlreadyInRegistry();
error AssetModNotUnique();
error AssetNotAllowed();
error InvalidAssetType();
error LengthMismatch();
error MaxRecursiveCallsReached();
error Min1Oracle();
error OnlyAccount();
error OnlyAssetModule();
error OnlyOracleModule();
error OracleModNotUnique();
error OracleNotReverting();
error OracleReverting();
error SequencerDown();
error Unauthorized();
error UnknownAsset();
}
/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { Proxy } from "./Proxy.sol";
import { IAccount } from "./interfaces/IAccount.sol";
import { IFactory } from "./interfaces/IFactory.sol";
import { IRegistry } from "./interfaces/IRegistry.sol";
import { ERC721, ERC721TokenReceiver } from "../lib/solmate/src/tokens/ERC721.sol";
import { Strings } from "./libraries/Strings.sol";
import { MerkleProofLib } from "../lib/solmate/src/utils/MerkleProofLib.sol";
import { FactoryGuardian } from "./guardians/FactoryGuardian.sol";
import { FactoryErrors } from "./libraries/Errors.sol";
/**
* @title Factory
* @author Pragma Labs
* @notice The Factory manages the deployment, upgrades and transfers of Arcadia Accounts.
* @dev The Factory is an ERC721 contract that maps each id to an Arcadia Account.
*/
contract Factory is IFactory, ERC721, FactoryGuardian {
using Strings for uint256;
/* //////////////////////////////////////////////////////////////
STORAGE
////////////////////////////////////////////////////////////// */
// The latest Account version, newly deployed Account use the latest version by default.
uint88 public latestAccountVersion;
// The baseURI of the ERC721 tokens.
string public baseURI;
// The Merkle root of the Merkle tree of all the compatible Account versions.
bytes32 public versionRoot;
// Array of all Arcadia Account contract addresses.
address[] public allAccounts;
// Map accountVersion => blocked status.
mapping(uint256 => bool) public accountVersionBlocked;
// Map accountAddress => accountIndex.
mapping(address => uint256) public accountIndex;
// Map accountVersion => version information.
mapping(uint256 => VersionInformation) public versionInformation;
// Struct with additional information for a specific Account version.
struct VersionInformation {
// The contract address of the Registry.
address registry;
// The contract address of the Account implementation.
address implementation;
// Arbitrary data, can contain instructions to execute when updating Account to new implementation.
bytes data;
}
/* //////////////////////////////////////////////////////////////
EVENTS
////////////////////////////////////////////////////////////// */
event AccountUpgraded(address indexed accountAddress, uint88 indexed newVersion);
event AccountVersionAdded(uint88 indexed version, address indexed registry, address indexed implementation);
event AccountVersionBlocked(uint88 version);
/* //////////////////////////////////////////////////////////////
CONSTRUCTOR
////////////////////////////////////////////////////////////// */
constructor() ERC721("Arcadia Account", "ARCADIA") { }
/*///////////////////////////////////////////////////////////////
ACCOUNT MANAGEMENT
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to create a new Account.
* @param salt A salt to be used to generate the hash.
* @param accountVersion The Account version.
* @param creditor The contract address of the creditor.
* @return account The contract address of the proxy contract of the newly deployed Account.
* @dev If accountVersion == 0, the newest version will be used.
* @dev createAccount() uses the CREATE2 opcode, which can lead to address collision vulnerabilities,
* as described in: https://eips.ethereum.org/EIPS/eip-3607.
* To decrease the probability of finding an address collision, the number of possible account addresses is limited to 2**64.
* We use a salt with 64 bits, the 32 right most bits of the address of the tx.origin, and 32 bits provided by the user.
*/
function createAccount(uint32 salt, uint256 accountVersion, address creditor)
external
whenCreateNotPaused
returns (address account)
{
accountVersion = accountVersion == 0 ? latestAccountVersion : accountVersion;
if (accountVersion > latestAccountVersion) revert FactoryErrors.InvalidAccountVersion();
if (accountVersionBlocked[accountVersion]) revert FactoryErrors.AccountVersionBlocked();
// Hash tx.origin with the user provided salt to avoid front-running Account deployment with an identical salt.
// We use tx.origin instead of msg.sender so that deployments through a third party contract are not vulnerable to front-running.
account = address(
// 64 bit salt: 32 bits from tx.origin and 32 bits provided by the user.
new Proxy{ salt: keccak256(abi.encodePacked(salt, uint32(uint160(tx.origin)))) }(
versionInformation[accountVersion].implementation
)
);
allAccounts.push(account);
accountIndex[account] = allAccounts.length;
_mint(msg.sender, allAccounts.length);
IAccount(account).initialize(msg.sender, versionInformation[accountVersion].registry, creditor);
// unsafe cast: accountVersion <= latestAccountVersion, which is a uint88.
emit AccountUpgraded(account, uint88(accountVersion));
}
/**
* @notice View function returning if an address is an Account.
* @param account The address to be checked.
* @return bool Whether the address is an Account or not.
*/
function isAccount(address account) public view returns (bool) {
return accountIndex[account] > 0;
}
/**
* @notice Returns the owner of an Account.
* @param account The Account address.
* @return owner_ The Account owner.
* @dev Function does not revert when a non-existing Account is passed, but returns zero-address as owner.
*/
function ownerOfAccount(address account) external view returns (address owner_) {
owner_ = _ownerOf[accountIndex[account]];
}
/**
* @notice This function allows Account owners to upgrade the implementation of the Account.
* @param account Account that needs to be upgraded.
* @param version The accountVersion to upgrade to.
* @param proofs The Merkle proofs that prove the compatibility of the upgrade from current to new account version.
* @dev As each Account is a proxy, the implementation of the proxy can be changed by the owner of the Account.
* Checks are done such that only compatible versions can be upgraded to.
* Merkle proofs and their leaves can be found on https://www.github.com/arcadia-finance.
*/
function upgradeAccountVersion(address account, uint256 version, bytes32[] calldata proofs) external {
if (_ownerOf[accountIndex[account]] != msg.sender) revert FactoryErrors.OnlyAccountOwner();
if (accountVersionBlocked[version]) revert FactoryErrors.AccountVersionBlocked();
uint256 currentVersion = IAccount(account).ACCOUNT_VERSION();
bool canUpgrade =
MerkleProofLib.verify(proofs, versionRoot, keccak256(abi.encodePacked(currentVersion, version)));
if (!canUpgrade) revert FactoryErrors.InvalidUpgrade();
IAccount(account).upgradeAccount(
versionInformation[version].implementation,
versionInformation[version].registry,
version,
versionInformation[version].data
);
// unsafe cast: accountVersion <= latestAccountVersion, which is a uint88.
emit AccountUpgraded(account, uint88(version));
}
/**
* @notice Function used to transfer an Account between users based on Account address.
* @param from The sender.
* @param to The target.
* @param account The address of the Account that is transferred.
* @dev This method transfers an Account on Account address instead of id and
* also transfers the Account proxy contract to the new owner.
* @dev The Account itself cannot become its owner.
*/
function safeTransferFrom(address from, address to, address account) public {
if (to == account) revert FactoryErrors.InvalidRecipient();
uint256 id = accountIndex[account];
IAccount(allAccounts[id - 1]).transferOwnership(to);
super.safeTransferFrom(from, to, id);
}
/**
* @notice Function used to transfer an Account between users based on Account id.
* @param from The sender.
* @param to The target.
* @param id The id of the Account that is about to be transferred.
* @dev This method overwrites the safeTransferFrom function in ERC721.sol to
* also transfer the Account proxy contract to the new owner.
* @dev The Account itself cannot become its owner.
*/
function safeTransferFrom(address from, address to, uint256 id) public override {
address account = allAccounts[id - 1];
if (to == account) revert FactoryErrors.InvalidRecipient();
IAccount(account).transferOwnership(to);
super.safeTransferFrom(from, to, id);
}
/**
* @notice Function used to transfer an Account between users based on Account id.
* @param from The sender.
* @param to The target.
* @param id The id of the Account that is about to be transferred.
* @param data additional data, only used for onERC721Received.
* @dev This method overwrites the safeTransferFrom function in ERC721.sol to
* also transfer the Account proxy contract to the new owner.
* @dev The Account itself cannot become its owner.
*/
function safeTransferFrom(address from, address to, uint256 id, bytes calldata data) public override {
address account = allAccounts[id - 1];
if (to == account) revert FactoryErrors.InvalidRecipient();
IAccount(account).transferOwnership(to);
super.safeTransferFrom(from, to, id, data);
}
/**
* @notice Function used to transfer an Account between users based on Account id.
* @param from The sender.
* @param to The target.
* @param id The id of the Account that is about to be transferred.
* @dev This method overwrites the transferFrom function in ERC721.sol to
* also transfer the Account proxy contract to the new owner.
* @dev The Account itself cannot become its owner.
*/
function transferFrom(address from, address to, uint256 id) public override {
address account = allAccounts[id - 1];
if (to == account) revert FactoryErrors.InvalidRecipient();
IAccount(account).transferOwnership(to);
super.transferFrom(from, to, id);
}
/**
* @notice Function used to transfer an Account, called by the Account itself.
* @param to The target.
* @dev Adaptation of safeTransferFrom from the ERC-721 standard, where the Account itself triggers the transfer.
* @dev The Account must do the transferOwnership() before calling this function.
* @dev The Account itself cannot become its owner.
*/
function safeTransferAccount(address to) public {
if (to == address(0)) revert FactoryErrors.InvalidRecipient();
if (to == msg.sender) revert FactoryErrors.InvalidRecipient();
uint256 id = accountIndex[msg.sender];
if (id == 0) revert FactoryErrors.OnlyAccount();
address from = _ownerOf[id];
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
if (
to.code.length != 0
&& ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "")
!= ERC721TokenReceiver.onERC721Received.selector
) revert FactoryErrors.UnsafeRecipient();
emit Transfer(from, to, id);
}
/*///////////////////////////////////////////////////////////////
ACCOUNT VERSION MANAGEMENT
///////////////////////////////////////////////////////////////*/
/**
* @notice Function to set a new Account version with the contracts to be used for new deployed Accounts.
* @param registry The contract address of the Registry.
* @param implementation The contract address of the Account implementation.
* @param versionRoot_ The Merkle root of the Merkle tree of all the compatible Account versions.
* @param data Arbitrary data, can contain instructions to execute when updating Account to new implementation.
* @dev Changing any of the contracts does NOT change the contracts for existing deployed Accounts,
* unless the Account owner explicitly chooses to upgrade their Account to a newer version.
*/
function setNewAccountInfo(address registry, address implementation, bytes32 versionRoot_, bytes calldata data)
external
onlyOwner
{
if (versionRoot_ == bytes32(0)) revert FactoryErrors.VersionRootIsZero();
if (implementation == address(0)) revert FactoryErrors.ImplIsZero();
uint256 latestAccountVersion_;
unchecked {
// Update and cache the new latestAccountVersion.
latestAccountVersion_ = ++latestAccountVersion;
}
versionRoot = versionRoot_;
versionInformation[latestAccountVersion_] =
VersionInformation({ registry: registry, implementation: implementation, data: data });
if (IAccount(implementation).ACCOUNT_VERSION() != latestAccountVersion) revert FactoryErrors.VersionMismatch();
if (IAccount(implementation).FACTORY() != address(this)) revert FactoryErrors.FactoryMismatch();
if (IRegistry(registry).FACTORY() != address(this)) revert FactoryErrors.FactoryMismatch();
emit AccountVersionAdded(uint88(latestAccountVersion_), registry, implementation);
}
/**
* @notice Function to block a certain Account implementation version from being created as a new Account.
* @param version The Account version to be phased out.
* @dev Should any Account implementation version be phased out,
* this function can be used to block it from being created for new Accounts.
* @dev Although possible to block an Account version through the versionRoot,
* that would require verifying the Merkle tree on account creation, which is expensive.
*/
function blockAccountVersion(uint256 version) external onlyOwner {
if (version == 0 || version > latestAccountVersion) revert FactoryErrors.InvalidAccountVersion();
accountVersionBlocked[version] = true;
// unsafe cast: accountVersion <= latestAccountVersion, which is a uint88.
emit AccountVersionBlocked(uint88(version));
}
/*///////////////////////////////////////////////////////////////
HELPER FUNCTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Function returns the total number of Accounts.
* @return numberOfAccounts The total number of Accounts.
*/
function allAccountsLength() external view returns (uint256 numberOfAccounts) {
numberOfAccounts = allAccounts.length;
}
/*///////////////////////////////////////////////////////////////
ERC-721 LOGIC
///////////////////////////////////////////////////////////////*/
/**
* @notice Function that stores a new base URI.
* @param newBaseURI The new base URI to store.
* @dev tokenURIs of Arcadia Accounts are not meant to be immutable
* and might be updated later to allow users to choose/create their own Account art,
* as such no URI freeze is added.
*/
function setBaseURI(string calldata newBaseURI) external onlyOwner {
baseURI = newBaseURI;
}
/**
* @notice Function that returns the token URI as defined in the ERC721 standard.
* @param tokenId The id of the Account.
* @return uri The token URI.
*/
function tokenURI(uint256 tokenId) public view override returns (string memory uri) {
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
}
/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity 0.8.22;
import { BaseGuardian, GuardianErrors } from "./BaseGuardian.sol";
/**
* @title Factory Guardian
* @author Pragma Labs
* @notice Logic inherited by the Factory that allows:
* - An authorized guardian to trigger an emergency stop.
* - The protocol owner to unpause functionalities.
*/
abstract contract FactoryGuardian is BaseGuardian {
/* //////////////////////////////////////////////////////////////
STORAGE
////////////////////////////////////////////////////////////// */
// Flag indicating if the create() function is paused.
bool public createPaused;
/* //////////////////////////////////////////////////////////////
ERRORS
////////////////////////////////////////////////////////////// */
error FunctionNotImplemented();
/* //////////////////////////////////////////////////////////////
EVENTS
////////////////////////////////////////////////////////////// */
event PauseFlagsUpdated(bool createPauseUpdate);
/* //////////////////////////////////////////////////////////////
MODIFIERS
////////////////////////////////////////////////////////////// */
/**
* @dev Throws if the createAccount functionality is paused.
*/
modifier whenCreateNotPaused() {
if (createPaused) revert GuardianErrors.FunctionIsPaused();
_;
}
/* //////////////////////////////////////////////////////////////
PAUSING LOGIC
////////////////////////////////////////////////////////////// */
/**
* @notice This function is used to pause the creation of Accounts.
* @dev The pause guardian of the Factory has no cool-down period.
*/
function pause() external override onlyGuardian {
emit PauseFlagsUpdated(createPaused = true);
}
/**
* @notice This function is used to unpause the creation of Accounts.
* @param createPaused_ "False" when create functionality should be unpaused.
* @dev This function can unpause the creation of new Accounts.
* @dev Can only update flags from paused (true) to unpaused (false), cannot be used the other way around
* (to set unpaused flags to paused).
*/
function unpause(bool createPaused_) external onlyOwner {
emit PauseFlagsUpdated(createPaused = createPaused && createPaused_);
}
/**
* @notice This function is not implemented.
* @dev No reason to be able to create an Account if the owner of the Factory did not unpause createAccount().
*/
function unpause() external pure override {
revert FunctionNotImplemented();
}
}
/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.22;
interface IAccount {
/**
* @notice Returns the Account version.
* @return version The Account version.
*/
function ACCOUNT_VERSION() external view returns (uint256);
/**
* @notice Returns the Arcadia Accounts Factory.
* @return factory The contract address of the Arcadia Accounts Factory.
*/
function FACTORY() external view returns (address);
/**
* @notice Initiates the variables of the Account.
* @param owner The sender of the 'createAccount' on the factory
* @param registry The 'beacon' contract with the external logic.
* @param creditor The contract address of the creditor.
*/
function initialize(address owner, address registry, address creditor) external;
/**
* @notice Updates the Account version and stores a new address in the EIP1967 implementation slot.
* @param newImplementation The contract with the new Account logic.
* @param newRegistry The Registry for this specific implementation (might be identical as the old registry).
* @param data Arbitrary data, can contain instructions to execute when updating Account to new logic.
* @param newVersion The new version of the Account logic.
*/
function upgradeAccount(address newImplementation, address newRegistry, uint256 newVersion, bytes calldata data)
external;
/**
* @notice Transfers ownership of the contract to a new account.
* @param newOwner The new owner of the Account.
*/
function transferOwnership(address newOwner) external;
}
/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.22;
interface IFactory {
/**
* @notice Checks if a contract is an Account.
* @param account The contract address of the Account.
* @return bool indicating if the address is an Account or not.
*/
function isAccount(address account) external view returns (bool);
/**
* @notice Function used to transfer an Account, called by the Account itself.
* @param to The target.
*/
function safeTransferAccount(address to) external;
}
/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity 0.8.22;
import { AssetValueAndRiskFactors } from "../libraries/AssetValuationLib.sol";
interface IRegistry {
/**
* @notice Returns the Arcadia Accounts Factory.
* @return factory The contract address of the Arcadia Accounts Factory.
*/
function FACTORY() external view returns (address);
/**
* @notice Checks if an asset is in the Registry.
* @param asset The contract address of the asset.
* @return boolean.
*/
function inRegistry(address asset) external view returns (bool);
/**
* @notice Batch retrieves the asset types.
* @param assetAddresses Array of the contract addresses of the assets.
* @return assetTypes Array with the types of the assets.
* 0 = Unknown asset.
* 1 = ERC20.
* 2 = ERC721.
* 3 = ERC1155.
* ...
*/
function batchGetAssetTypes(address[] calldata assetAddresses) external view returns (uint256[] memory);
/**
* @notice Batch deposit multiple assets.
* @param creditor The contract address of the creditor.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param amounts Array with the amounts of the assets.
*/
function batchProcessDeposit(
address creditor,
address[] calldata assetAddresses,
uint256[] calldata assetIds,
uint256[] calldata amounts
) external;
/**
* @notice Batch withdraw multiple assets.
* @param creditor The contract address of the creditor.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param amounts Array with the amounts of the assets.
* @return assetTypes Array with the types of the assets.
* 0 = Unknown asset.
* 1 = ERC20.
* 2 = ERC721.
* 3 = ERC1155.
*/
function batchProcessWithdrawal(
address creditor,
address[] calldata assetAddresses,
uint256[] calldata assetIds,
uint256[] calldata amounts
) external returns (uint256[] memory);
/**
* @notice Calculates the combined value of a combination of assets, denominated in a given Numeraire.
* @param numeraire The contract address of the Numeraire.
* @param creditor The contract address of the creditor.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param assetAmounts Array with the amounts of the assets.
* @return assetValue The combined value of the assets, denominated in the Numeraire.
*/
function getTotalValue(
address numeraire,
address creditor,
address[] calldata assetAddresses,
uint256[] calldata assetIds,
uint256[] calldata assetAmounts
) external view returns (uint256);
/**
* @notice Calculates the collateralValue of a combination of assets, denominated in a given Numeraire.
* @param numeraire The contract address of the Numeraire.
* @param creditor The contract address of the creditor.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param assetAmounts Array with the amounts of the assets.
* @return collateralValue The collateral value of the assets, denominated in the Numeraire.
*/
function getCollateralValue(
address numeraire,
address creditor,
address[] calldata assetAddresses,
uint256[] calldata assetIds,
uint256[] calldata assetAmounts
) external view returns (uint256);
/**
* @notice Calculates the getLiquidationValue of a combination of assets, denominated in a given Numeraire.
* @param numeraire The contract address of the Numeraire.
* @param creditor The contract address of the creditor.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param assetAmounts Array with the amounts of the assets.
* @return liquidationValue The liquidation value of the assets, denominated in the Numeraire.
*/
function getLiquidationValue(
address numeraire,
address creditor,
address[] calldata assetAddresses,
uint256[] calldata assetIds,
uint256[] calldata assetAmounts
) external view returns (uint256);
/**
* @notice Calculates the values per asset, denominated in a given Numeraire.
* @param numeraire The contract address of the Numeraire.
* @param creditor The contract address of the creditor.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param assetAmounts Array with the amounts of the assets.
* @return valuesAndRiskFactors The array of values per assets, denominated in the Numeraire.
*/
function getValuesInNumeraire(
address numeraire,
address creditor,
address[] calldata assetAddresses,
uint256[] calldata assetIds,
uint256[] calldata assetAmounts
) external view returns (AssetValueAndRiskFactors[] memory valuesAndRiskFactors);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/// @notice Gas optimized merkle proof verification library.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
library MerkleProofLib {
function verify(
bytes32[] calldata proof,
bytes32 root,
bytes32 leaf
) internal pure returns (bool isValid) {
/// @solidity memory-safe-assembly
assembly {
if proof.length {
// Left shifting by 5 is like multiplying by 32.
let end := add(proof.offset, shl(5, proof.length))
// Initialize offset to the offset of the proof in calldata.
let offset := proof.offset
// Iterate over proof elements to compute root hash.
// prettier-ignore
for {} 1 {} {
// Slot where the leaf should be put in scratch space. If
// leaf > calldataload(offset): slot 32, otherwise: slot 0.
let leafSlot := shl(5, gt(leaf, calldataload(offset)))
// Store elements to hash contiguously in scratch space.
// The xor puts calldataload(offset) in whichever slot leaf
// is not occupying, so 0 if leafSlot is 32, and 32 otherwise.
mstore(leafSlot, leaf)
mstore(xor(leafSlot, 32), calldataload(offset))
// Reuse leaf to store the hash to reduce stack operations.
leaf := keccak256(0, 64) // Hash both slots of scratch space.
offset := add(offset, 32) // Shift 1 word per cycle.
// prettier-ignore
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root) // The proof is valid if the roots match.
}
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event OwnershipTransferred(address indexed user, address indexed newOwner);
/*//////////////////////////////////////////////////////////////
OWNERSHIP STORAGE
//////////////////////////////////////////////////////////////*/
address public owner;
modifier onlyOwner() virtual {
require(msg.sender == owner, "UNAUTHORIZED");
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferred(address(0), _owner);
}
/*//////////////////////////////////////////////////////////////
OWNERSHIP LOGIC
//////////////////////////////////////////////////////////////*/
function transferOwnership(address newOwner) public virtual onlyOwner {
owner = newOwner;
emit OwnershipTransferred(msg.sender, newOwner);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.22;
/**
* @title Proxy
* @author Pragma Labs
* @dev Implementation based on ERC1967: Proxy Storage Slots.
* See https://eips.ethereum.org/EIPS/eip-1967.
*/
contract Proxy {
/* //////////////////////////////////////////////////////////////
CONSTANTS
////////////////////////////////////////////////////////////// */
// Storage slot with the address of the current implementation.
// This is the hardcoded keccak-256 hash of: "eip1967.proxy.implementation" subtracted by 1.
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/* //////////////////////////////////////////////////////////////
STORAGE
////////////////////////////////////////////////////////////// */
// Storage slot for the Account logic, a struct to avoid storage conflict when dealing with upgradeable contracts.
struct AddressSlot {
address value;
}
/* //////////////////////////////////////////////////////////////
EVENTS
////////////////////////////////////////////////////////////// */
event Upgraded(address indexed implementation);
/* //////////////////////////////////////////////////////////////
CONSTRUCTOR
////////////////////////////////////////////////////////////// */
/**
* @param implementation The contract address of the Account logic.
*/
constructor(address implementation) payable {
_getAddressSlot(IMPLEMENTATION_SLOT).value = implementation;
emit Upgraded(implementation);
}
/**
* @dev Fallback function that delegates calls to the implementation address.
* Will run if call data is empty.
*/
receive() external payable virtual {
_delegate(_getAddressSlot(IMPLEMENTATION_SLOT).value);
}
/**
* @dev Fallback function that delegates calls to the implementation address.
* Will run if no other function in the contract matches the call data.
*/
fallback() external payable virtual {
_delegate(_getAddressSlot(IMPLEMENTATION_SLOT).value);
}
/*///////////////////////////////////////////////////////////////
IMPLEMENTATION LOGIC
///////////////////////////////////////////////////////////////*/
/**
* @notice Returns the "AddressSlot" with member "value" located at "slot".
* @param slot The slot where the address of the Logic contract is stored.
* @return r The address stored in slot.
*/
function _getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly {
r.slot := slot
}
}
/*///////////////////////////////////////////////////////////////
DELEGATION LOGIC
///////////////////////////////////////////////////////////////*/
/**
* @param implementation The contract address of the logic.
* @dev Delegates the current call to "implementation".
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal virtual {
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.22;
library Strings {
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
}
{
"compilationTarget": {
"src/Factory.sol": "Factory"
},
"evmVersion": "shanghai",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin/=lib/openzeppelin-contracts/",
":@uniswap/v3-core/contracts/=lib/v3-core/contracts/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
":solmate/=lib/solmate/src/",
":v3-core/=lib/v3-core/",
":v3-periphery/=lib/v3-periphery/contracts/"
]
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountVersionBlocked","type":"error"},{"inputs":[],"name":"FactoryMismatch","type":"error"},{"inputs":[],"name":"FunctionIsPaused","type":"error"},{"inputs":[],"name":"FunctionNotImplemented","type":"error"},{"inputs":[],"name":"ImplIsZero","type":"error"},{"inputs":[],"name":"InvalidAccountVersion","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidUpgrade","type":"error"},{"inputs":[],"name":"OnlyAccount","type":"error"},{"inputs":[],"name":"OnlyAccountOwner","type":"error"},{"inputs":[],"name":"OnlyGuardian","type":"error"},{"inputs":[],"name":"UnsafeRecipient","type":"error"},{"inputs":[],"name":"VersionMismatch","type":"error"},{"inputs":[],"name":"VersionRootIsZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"accountAddress","type":"address"},{"indexed":true,"internalType":"uint88","name":"newVersion","type":"uint88"}],"name":"AccountUpgraded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint88","name":"version","type":"uint88"},{"indexed":true,"internalType":"address","name":"registry","type":"address"},{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"AccountVersionAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint88","name":"version","type":"uint88"}],"name":"AccountVersionBlocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newGuardian","type":"address"}],"name":"GuardianChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"createPauseUpdate","type":"bool"}],"name":"PauseFlagsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"accountIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"accountVersionBlocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allAccounts","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allAccountsLength","outputs":[{"internalType":"uint256","name":"numberOfAccounts","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"}],"name":"blockAccountVersion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"guardian_","type":"address"}],"name":"changeGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"salt","type":"uint32"},{"internalType":"uint256","name":"accountVersion","type":"uint256"},{"internalType":"address","name":"creditor","type":"address"}],"name":"createAccount","outputs":[{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"createPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isAccount","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"latestAccountVersion","outputs":[{"internalType":"uint88","name":"","type":"uint88"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ownerOfAccount","outputs":[{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseTimestamp","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"safeTransferAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"account","type":"address"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"newBaseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"registry","type":"address"},{"internalType":"address","name":"implementation","type":"address"},{"internalType":"bytes32","name":"versionRoot_","type":"bytes32"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"setNewAccountInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"uri","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bool","name":"createPaused_","type":"bool"}],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"version","type":"uint256"},{"internalType":"bytes32[]","name":"proofs","type":"bytes32[]"}],"name":"upgradeAccountVersion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"versionInformation","outputs":[{"internalType":"address","name":"registry","type":"address"},{"internalType":"address","name":"implementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"versionRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"}]