APE Price: $0.75 (+1.87%)

Contract Diff Checker

Contract Name:
CyanWalletLogic

Contract Source Code:

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

pragma solidity ^0.8.0;

import "../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.
 *
 * By default, the owner account will be the one that deploys the contract. 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;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @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 {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing 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 {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _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 v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

enum ConduitItemType {
    NATIVE, // unused
    ERC20,
    ERC721,
    ERC1155
}

struct ConduitTransfer {
    ConduitItemType itemType;
    address collection;
    address from;
    address to;
    uint256 identifier;
    uint256 amount;
}

struct ConduitBatch1155Transfer {
    address collection;
    address from;
    address to;
    uint256[] ids;
    uint256[] amounts;
}

interface ICyanConduit {
    error ChannelClosed(address channel);
    error ChannelStatusAlreadySet(address channel, bool isOpen);
    error InvalidItemType();
    error InvalidAdmin();

    event ChannelUpdated(address indexed channel, bool open);

    function execute(ConduitTransfer[] calldata transfers) external returns (bytes4 magicValue);

    function executeBatch1155(ConduitBatch1155Transfer[] calldata batch1155Transfers)
        external
        returns (bytes4 magicValue);

    function executeWithBatch1155(
        ConduitTransfer[] calldata standardTransfers,
        ConduitBatch1155Transfer[] calldata batch1155Transfers
    ) external returns (bytes4 magicValue);

    function transferERC20(
        address from,
        address to,
        address token,
        uint256 amount
    ) external;

    function transferERC721(
        address from,
        address to,
        address collection,
        uint256 tokenId
    ) external;

    function transferERC1155(
        address from,
        address to,
        address collection,
        uint256 tokenId,
        uint256 amount
    ) external;

    function updateChannel(address channel, bool isOpen) external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { Item } from "../../main/payment-plan/PaymentPlanTypes.sol";

interface IWallet {
    function executeModule(bytes memory) external returns (bytes memory);

    function transferNonLockedERC721(
        address,
        uint256,
        address
    ) external;

    function transferNonLockedERC1155(
        address,
        uint256,
        uint256,
        address
    ) external;

    function transferNonLockedCryptoPunk(uint256, address) external;

    function setLockedERC721Token(
        address,
        uint256,
        bool
    ) external;

    function increaseLockedERC1155Token(
        address,
        uint256,
        uint256
    ) external;

    function decreaseLockedERC1155Token(
        address,
        uint256,
        uint256
    ) external;

    function setLockedCryptoPunk(uint256, bool) external;

    function autoPay(
        uint256,
        uint256,
        uint8
    ) external;

    function earlyUnwindOpensea(
        uint256,
        uint256,
        Item memory,
        bytes memory
    ) external;

    function earlyUnwindCyan(uint256, address) external;

    function isLockedNFT(address, uint256) external view returns (bool);

    function repayBendDaoLoan(
        address collection,
        uint256 tokenId,
        uint256 amount,
        address currency
    ) external;
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface ICyanPeerPlan {
    enum PlanStatus {
        NONE,
        ACTIVE,
        DEFAULTED,
        COMPLETED,
        LIQUIDATED
    }
    struct LenderSignature {
        uint256 signedDate;
        uint256 expiryDate;
        uint32 maxUsageCount;
        bool extendable;
        bytes signature;
    }
    struct Plan {
        uint256 amount;
        address lenderAddress;
        address currencyAddress;
        uint32 interestRate;
        uint32 serviceFeeRate;
        uint32 term;
    }
    struct PaymentPlan {
        Plan plan;
        uint256 dueDate;
        address cyanWalletAddress;
        PlanStatus status;
        bool extendable;
    }
    struct Item {
        uint256 amount;
        uint256 tokenId;
        address contractAddress;
        // 1 -> ERC721
        // 2 -> ERC1155
        // 3 -> CryptoPunks
        uint8 itemType;
        bytes collectionSignature;
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";

/// @title Cyan AddressProvider contract
/// @author Bulgantamir Gankhuyag - <[email protected]>
/// @author Naranbayar Uuganbayar - <[email protected]>
contract AddressProvider is Ownable {
    error AddressNotFound(bytes32 id);

    event AddressSet(bytes32 id, address newAddress);

    mapping(bytes32 => address) public addresses;

    constructor(address owner) {
        transferOwnership(owner);
    }

    // @dev Sets an address for an id replacing the address saved in the addresses map
    // @param id The id
    // @param newAddress The address to set
    function setAddress(bytes32 id, address newAddress) external onlyOwner {
        addresses[id] = newAddress;
        emit AddressSet(id, newAddress);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { ICyanConduit } from "../interfaces/conduit/ICyanConduit.sol";
import { AddressProvider } from "../main/AddressProvider.sol";
import "../thirdparty/ICryptoPunk.sol";
import "../interfaces/core/IWallet.sol";
import "../interfaces/main/ICyanPeerPlan.sol";
import "./payment-plan/PaymentPlanTypes.sol";

library CyanWalletLogic {
    AddressProvider private constant addressProvider = AddressProvider(0xCF9A19D879769aDaE5e4f31503AAECDa82568E55);

    /**
     * @notice Allows operators to transfer out non locked tokens.
     *     Note: Can only transfer if token is not locked.
     * @param cyanWalletAddress Cyan Wallet address
     * @param to Receiver address
     * @param item Transferring item
     */
    function transferNonLockedItem(
        address cyanWalletAddress,
        address to,
        Item calldata item
    ) external {
        _transferNonLockedItem(cyanWalletAddress, to, item.contractAddress, item.tokenId, item.amount, item.itemType);
    }

    /**
     * @notice Allows operators to transfer out non locked tokens.
     *     Note: Can only transfer if token is not locked.
     * @param cyanWalletAddress Cyan Wallet address
     * @param to Receiver address
     * @param item Transferring item
     */
    function transferNonLockedItem(
        address cyanWalletAddress,
        address to,
        ICyanPeerPlan.Item calldata item
    ) external {
        _transferNonLockedItem(cyanWalletAddress, to, item.contractAddress, item.tokenId, item.amount, item.itemType);
    }

    function _transferNonLockedItem(
        address cyanWalletAddress,
        address to,
        address collection,
        uint256 tokenId,
        uint256 amount,
        uint8 itemType
    ) private {
        IWallet wallet = IWallet(cyanWalletAddress);
        if (itemType == 1) {
            // ERC721
            wallet.executeModule(
                abi.encodeWithSelector(IWallet.transferNonLockedERC721.selector, collection, tokenId, to)
            );
        } else if (itemType == 2) {
            // ERC1155
            wallet.executeModule(
                abi.encodeWithSelector(IWallet.transferNonLockedERC1155.selector, collection, tokenId, amount, to)
            );
        } else if (itemType == 3) {
            // CryptoPunks
            wallet.executeModule(abi.encodeWithSelector(IWallet.transferNonLockedCryptoPunk.selector, tokenId, to));
        } else {
            revert InvalidItem();
        }
    }

    /**
     * @notice Transfers token to CyanWallet and locks it
     * @param from From address
     * @param cyanWalletAddress Cyan Wallet address
     * @param item Transferring item
     */
    function transferItemAndLock(
        address from,
        address cyanWalletAddress,
        Item calldata item
    ) external {
        _transferItemAndLock(from, cyanWalletAddress, item.contractAddress, item.tokenId, item.amount, item.itemType);
    }

    /**
     * @notice Transfers token to CyanWallet and locks it
     * @param from From address
     * @param cyanWalletAddress Cyan Wallet address
     * @param item Transferring item
     */
    function transferItemAndLock(
        address from,
        address cyanWalletAddress,
        ICyanPeerPlan.Item calldata item
    ) external {
        _transferItemAndLock(from, cyanWalletAddress, item.contractAddress, item.tokenId, item.amount, item.itemType);
    }

    function _transferItemAndLock(
        address from,
        address cyanWalletAddress,
        address collection,
        uint256 tokenId,
        uint256 amount,
        uint8 itemType
    ) private {
        if (itemType == 3) {
            // CryptoPunks
            ICryptoPunk cryptoPunkContract = ICryptoPunk(collection);
            if (cryptoPunkContract.punkIndexToAddress(tokenId) != from) revert InvalidItem();
            cryptoPunkContract.buyPunk{ value: 0 }(tokenId);
            cryptoPunkContract.transferPunk(cyanWalletAddress, tokenId);
        } else {
            ICyanConduit conduit = ICyanConduit(addressProvider.addresses("CYAN_CONDUIT"));
            if (itemType == 1) {
                conduit.transferERC721(from, cyanWalletAddress, collection, tokenId);
            } else if (itemType == 2) {
                conduit.transferERC1155(from, cyanWalletAddress, collection, tokenId, amount);
            } else {
                revert InvalidItem();
            }
        }

        _setLockState(cyanWalletAddress, collection, tokenId, amount, itemType, true);
    }

    /**
     * @notice Update locking status of a token in Cyan Wallet
     * @param cyanWalletAddress Cyan Wallet address
     * @param item Locking/unlocking item
     * @param state Token will be locked if true
     */
    function setLockState(
        address cyanWalletAddress,
        Item calldata item,
        bool state
    ) public {
        _setLockState(cyanWalletAddress, item.contractAddress, item.tokenId, item.amount, item.itemType, state);
    }

    /**
     * @notice Update locking status of a token in Cyan Wallet
     * @param cyanWalletAddress Cyan Wallet address
     * @param item Locking/unlocking item
     * @param state Token will be locked if true
     */
    function setLockState(
        address cyanWalletAddress,
        ICyanPeerPlan.Item calldata item,
        bool state
    ) public {
        _setLockState(cyanWalletAddress, item.contractAddress, item.tokenId, item.amount, item.itemType, state);
    }

    function _setLockState(
        address cyanWalletAddress,
        address collection,
        uint256 tokenId,
        uint256 amount,
        uint8 itemType,
        bool state
    ) private {
        IWallet wallet = IWallet(cyanWalletAddress);
        if (itemType == 1) {
            // ERC721
            wallet.executeModule(
                abi.encodeWithSelector(IWallet.setLockedERC721Token.selector, collection, tokenId, state)
            );
        } else if (itemType == 2) {
            // ERC1155
            wallet.executeModule(
                abi.encodeWithSelector(
                    state ? IWallet.increaseLockedERC1155Token.selector : IWallet.decreaseLockedERC1155Token.selector,
                    collection,
                    tokenId,
                    amount
                )
            );
        } else if (itemType == 3) {
            // CryptoPunks
            wallet.executeModule(abi.encodeWithSelector(IWallet.setLockedCryptoPunk.selector, tokenId, state));
        } else {
            revert InvalidItem();
        }
    }

    /**
     * @notice Triggers Cyan Wallet's autoPay method
     * @param cyanWalletAddress Cyan Wallet address
     * @param planId Payment plan ID
     * @param amount Pay amount for the plan
     * @param autoRepayStatus Auto repayment status
     */
    function executeAutoPay(
        address cyanWalletAddress,
        uint256 planId,
        uint256 amount,
        uint8 autoRepayStatus
    ) external {
        IWallet(cyanWalletAddress).executeModule(
            abi.encodeWithSelector(IWallet.autoPay.selector, planId, amount, autoRepayStatus)
        );
    }
}

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

// DataTypes
enum PawnCreateType {
    REGULAR,
    BEND_DAO,
    REFINANCE
}
enum PaymentPlanStatus {
    BNPL_CREATED,
    BNPL_FUNDED,
    BNPL_ACTIVE,
    BNPL_DEFAULTED,
    BNPL_REJECTED,
    BNPL_COMPLETED,
    BNPL_LIQUIDATED,
    PAWN_ACTIVE,
    PAWN_DEFAULTED,
    PAWN_COMPLETED,
    PAWN_LIQUIDATED
}
struct Plan {
    uint256 amount;
    uint32 downPaymentPercent;
    uint32 interestRate;
    uint32 serviceFeeRate;
    uint32 term;
    uint8 totalNumberOfPayments;
    uint8 counterPaidPayments;
    uint8 autoRepayStatus;
}
struct PaymentPlan {
    Plan plan;
    uint256 createdDate;
    address cyanWalletAddress;
    PaymentPlanStatus status;
}

struct Item {
    uint256 amount;
    uint256 tokenId;
    address contractAddress;
    address cyanVaultAddress;
    // 1 -> ERC721
    // 2 -> ERC1155
    // 3 -> CryptoPunks
    uint8 itemType;
}

struct PaymentAmountInfo {
    uint256 loanAmount;
    uint256 interestAmount;
    uint256 serviceAmount;
}

// Errors
error InvalidSender();
error InvalidBlockNumber();
error InvalidSignature();
error InvalidServiceFeeRate();
error InvalidTokenPrice();
error InvalidInterestRate();
error InvalidDownPaymentPercent();
error InvalidDownPayment();
error InvalidAmount();
error InvalidTerm();
error InvalidPaidCount();
error InvalidStage();
error InvalidAddress();
error InvalidAutoRepaymentDate();
error InvalidAutoRepaymentStatus();
error InvalidTotalNumberOfPayments();
error InvalidReviveDate();
error InvalidItem();
error InvalidBaseDiscountRate();
error InvalidApeCoinPlan();
error InvalidBendDaoPlan();
error InvalidCurrency();
error InvalidCyanBuyer();
error InvalidSelector();

error EthTransferFailed();

error PaymentPlanAlreadyExists();
error PaymentPlanNotFound();

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface ICryptoPunk {
    function punkIndexToAddress(uint256) external view returns (address);

    function buyPunk(uint256) external payable;

    function transferPunk(address, uint256) external;

    function offerPunkForSale(uint256, uint256) external;

    function offerPunkForSaleToAddress(
        uint256,
        uint256,
        address
    ) external;

    function acceptBidForPunk(uint256, uint256) external;
}

Please enter a contract address above to load the contract details and source code.

Context size (optional):