Skip to content

  • Projects
  • Groups
  • Snippets
  • Help
    • Loading...
    • Help
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
D
docs
  • Project
    • Project
    • Details
    • Activity
    • Releases
    • Cycle Analytics
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
    • Charts
  • Issues 3
    • Issues 3
    • List
    • Boards
    • Labels
    • Milestones
  • Merge Requests 0
    • Merge Requests 0
  • CI / CD
    • CI / CD
    • Pipelines
    • Jobs
    • Schedules
    • Charts
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Members
    • Members
  • Collapse sidebar
  • Activity
  • Graph
  • Charts
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
  • jack
  • docs
  • Issues
  • #2

Closed
Open
Opened Dec 26, 2024 by jack@aaa7010aaa
  • Report abuse
  • New issue
Report abuse New issue

合约代码


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

import "@openzeppelin/contracts/utils/Create2.sol";

import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {ERC1155, IERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import {ERC1155Burnable} from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol";
import {ERC1155Supply} from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

//only for base
address constant VAULTFACTORY = 0x402949029F11bB31cEBde6Ca7E2D8cEccEB00a2F;
address constant VAULTMANAGEMODULE = 0x28F2600c10fEF2c8dB45e2eA89b085bdE7E22411;
address constant ISSUANCEMODULE = 0x26d11e8C94E342FEcFA7E8826e5fA03a31FB0a3C;

address constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

address constant CBBTC = 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf;

interface IJasperOption {
    /**
     * execute a sequence of transactions
     */
    function executeBatch(
        address[] calldata dest,
        uint256[] calldata value,
        bytes[] calldata func
    ) external returns (bytes[] memory results);
    function createAccount(address wallet, uint256 salt) external returns (address ret); // vault

    struct PremiumOracleSign {
        uint256 id;
        uint256 chainId;
        uint64 productType;
        address optionAsset;
        uint256 strikePrice;
        address strikeAsset;
        uint256 strikeAmount;
        address lockAsset;
        uint256 lockAmount;
        uint256 expireDate;
        uint256 lockDate;
        uint8 optionType;
        address premiumAsset;
        uint256 premiumFee;
        uint256 timestamp;
        bytes[] oracleSign;
    }

    struct ManagedOrder {
        address holder;
        address writer;
        address recipient;
        uint256 quantity;
        uint256 settingsIndex;
        uint256 productTypeIndex;
        uint256 oracleIndex;
        address nftFreeOption;
        PremiumOracleSign premiumSign;
        uint8 optionSourceType;
        bool liquidationToEOA;
        uint256 offerID;
    }
    function SubmitManagedOrder(ManagedOrder memory _info) external;
}

contract OptionPositionToken is ERC1155, Ownable, ERC1155Supply {
    using EnumerableSet for EnumerableSet.UintSet;

    constructor() ERC1155("") Ownable(msg.sender) {}

    string public baseUri = "https://jasper-simple-be.deno.dev/items/";

    function setURI(string memory newuri) public onlyOwner {
        baseUri = newuri;
    }

    function uri(uint256 id) public view override returns (string memory) {
        return string.concat(baseUri, Strings.toString(id), ".json");
    }

    uint256 public _nextTokenId = 1;

    struct TokenInfo {
        address aa_owner;
        uint total;
    }
    mapping(uint => TokenInfo) public nft_to_info; // nft id -> info

    // The following functions are overrides required by Solidity.
    function _update(address from, address to, uint256[] memory ids, uint256[] memory values) internal override(ERC1155, ERC1155Supply) {
        super._update(from, to, ids, values);
    }

    event CreateNFTPosition(
        address indexed sender,
        address indexed aa_owner,
        address indexed aa_owner_aa_vault,
        uint token_id,
        uint token_amount
    );

    //create position
    function mint(address[] calldata dest, uint256[] calldata value, bytes[] calldata func) public {
        uint premium;
        uint amountPerShare;
        uint total_amount;
        address premiumAsset;
        //Stack too deep
        {
            //get order info using last calldata
            IJasperOption.ManagedOrder memory info = abi.decode(extractCalldata(func[func.length - 1]), (IJasperOption.ManagedOrder));
            premium = (info.premiumSign.premiumFee * info.quantity) / 1e18;
            amountPerShare = 0.01 * 1e18;
            {
                address optionAsset = info.premiumSign.optionAsset;
                if (optionAsset == CBBTC) {
                    amountPerShare = 0.001 * 1e18; //0.001 BTC = 1 share
                }
            }
            require(info.quantity % amountPerShare == 0, "can't split amount");
            total_amount = (info.quantity / amountPerShare);
            premiumAsset = info.premiumSign.premiumAsset;
        }

        IERC20(premiumAsset).transferFrom(msg.sender, address(this), premium); //一层层传递给 AA vault
        _createPosition(msg.sender, _nextTokenId++, total_amount, premiumAsset, premium, dest, value, func);
    }

    function _createPosition(
        address user,
        uint tokenId,
        uint total_amount,
        address premiumAsset,
        uint premium,
        address[] calldata dest,
        uint256[] calldata value,
        bytes[] calldata func
    ) internal {
        AAVaultOwner aa_owner = new AAVaultOwner{salt: keccak256(abi.encodePacked(uint(tokenId)))}();
        nft_to_info[tokenId] = TokenInfo({aa_owner: address(aa_owner), total: total_amount});
        IERC20(premiumAsset).transfer(address(aa_owner), premium); //一层层传递给 AA vault
        address aa_vault_address = aa_owner.init(address(this), tokenId, premiumAsset, premium, dest, value, func);
        _mint(user, tokenId, total_amount, "");
        emit CreateNFTPosition(user, address(aa_owner), aa_vault_address, tokenId, total_amount);
    }

    //claim porift
    function burn(uint tokenId, uint amount, address vault_asset) public {
        address user = msg.sender;
        TokenInfo storage tokenInfo = nft_to_info[tokenId];
        AAVaultOwner(tokenInfo.aa_owner).redeem(user, tokenInfo.total, amount, vault_asset);
        _burn(user, tokenId, amount);
    }

    //reads
    function calculateAddr(uint tokenId) public view returns (address predictedAddress) {
        bytes32 salt = keccak256(abi.encodePacked(uint(tokenId)));
        predictedAddress = address(
            uint160(uint(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, keccak256(type(AAVaultOwner).creationCode)))))
        );
    }

    // utils
    function extractCalldata(bytes memory calldataWithSelector) internal pure returns (bytes memory) {
        bytes memory calldataWithoutSelector;

        require(calldataWithSelector.length >= 4);

        assembly {
            let totalLength := mload(calldataWithSelector)
            let targetLength := sub(totalLength, 4)
            calldataWithoutSelector := mload(0x40)

            // Set the length of callDataWithoutSelector (initial length - 4)
            mstore(calldataWithoutSelector, targetLength)

            // Mark the memory space taken for callDataWithoutSelector as allocated
            mstore(0x40, add(calldataWithoutSelector, add(0x20, targetLength)))

            // Process first 32 bytes (we only take the last 28 bytes)
            mstore(add(calldataWithoutSelector, 0x20), shl(0x20, mload(add(calldataWithSelector, 0x20))))

            // Process all other data by chunks of 32 bytes
            for {
                let i := 0x1C
            } lt(i, targetLength) {
                i := add(i, 0x20)
            } {
                mstore(add(add(calldataWithoutSelector, 0x20), i), mload(add(add(calldataWithSelector, 0x20), add(i, 0x04))))
            }
        }

        return calldataWithoutSelector;
    }
}

contract AAVaultOwner {
    address public nft_address;
    uint public tokenId;

    address public aa_vault;
    address public option_vault;

    bool internal initialized = false;

    function init(
        address _nft_address,
        uint _tokenId,
        address premium_asset,
        uint premium_amount,
        address[] calldata dest,
        uint256[] calldata value,
        bytes[] calldata func
    ) public returns (address aa_vault_address) {
        require(!initialized, "already initialized");
        initialized = true;

        nft_address = _nft_address;
        tokenId = _tokenId;

        address user = address(this); //等同于 目前 jasper 的用户

        // step1  creat aa vault for this owner
        aa_vault = IJasperOption(VAULTFACTORY).createAccount(user, 1); //钱在本合约这里, 需要充值/提现到 aa_vault 里
        IERC20(premium_asset).transfer(address(aa_vault), premium_amount); //一层层传递给 AA vault , 需要优化

        //在客户端里具体操作
        //create position
        IJasperOption(aa_vault).executeBatch(dest, value, func);
        aa_vault_address = aa_vault;
    }

    function redeem(address nft_owner, uint total_share, uint share, address asset) public {
        require(msg.sender == nft_address, "only nft contract can call");

        //TODO danger 只能在仓位结算完成后才能调用这个方法

        bool is_native_token = asset == NATIVE_TOKEN;
        uint vault_amount = is_native_token ? aa_vault.balance : IERC20(asset).balanceOf(aa_vault);
        require(vault_amount > 0, "no profit");
        uint amount = (share * vault_amount) / total_share;

        //aa vault to vault owner
        address[] memory assets = new address[](1);
        assets[0] = asset;
        uint[] memory asset_types = new uint[](1);
        asset_types[0] = 1;

        uint[] memory amounts = new uint[](1);
        amounts[0] = amount;

        address[] memory st_targets = new address[](1);
        st_targets[0] = ISSUANCEMODULE;

        uint[] memory st_values = new uint[](1);
        st_values[0] = 0;

        bytes[] memory st_funcs = new bytes[](1);
        st_funcs[0] = abi.encodeWithSignature("redeem(address,uint256[],address[],uint256[])", aa_vault, asset_types, assets, amounts);
        IJasperOption(aa_vault).executeBatch(st_targets, st_values, st_funcs);

        //vault owner to current nft owner
        if (is_native_token) {
            Address.sendValue(payable(nft_owner), amount);
        } else {
            IERC20(asset).transfer(nft_owner, amount);
        }
    }
}


Edited Dec 30, 2024 by jack
Assignee
Assign to
None
Milestone
None
Assign milestone
Time tracking
None
Due date
None
0
Labels
None
Assign labels
  • View project labels
Reference: aaa7010aaa/docs#2