拍得到-智能合约

拍得到智能合约流程

先把逻辑捋顺再看代码

在这里插入图片描述

在这里插入图片描述

竞标规则

  1. 仅可在竞标时间段内竞标,竞标结束后⽆法再次竞标,竞标⻚⾯关闭

  2. 每个⼈都可以参与竞标。(卖家⾃⼰也可以参与竞标)

  3. 每个⼈可以多次对同⼀产品竞标。(我可以看到别⼈⽐我⾼的时候,再次出价;可以使⽤战略, 多次出价迷惑敌⼈)

  4. 流程:
    每个⼈竞标时输⼊两个数字和⼀个密码:

迷惑价格:30元(打到合约的⾦额,后⾯揭标时会退还差价,公开)

理想出价:10元(隐秘,不会在竞标函数中作为显示参数传⼊)

密码字符串(隐秘)

系统记录相关信息

迷惑价格必须⼤于理想价格时,否则视为⽆效竞标,⽆效竞标可以提交,⽤于迷惑竞争者,揭标 时⾃动退款。

竞标成功的⼈最终会以 次⾼价 购买商品(买家花费的钱⽐⼼理预期少,卖家⾄少会得到起拍 价)。

  1. 揭标规则

  2. 仅限于 竞标结束 和 终结拍卖 之前这段时间内完成

  3. 流程: i. 每个⼈输⼊⼀个数字和⼀个密码: i. 理想出价(同竞标时的数字) ii. 密码(同竞标时的字符串) ii. 揭标时会⼀直动态更新产品的最⾼的出价⼈、最⾼价、次⾼价格。 iii. 淘汰的⼈得到退款(当初转⼊的 迷惑价格 )。 iv. 最⾼价的⼈得到迷惑价格与理想出价的差额。

  4. 如果参与竞标但是未及时揭标,⽆法⾃动退款

  5. 终结竞拍规则

  6. 由任意⼀个第三⽅的⼈来执⾏终结。

  7. 流程: i. 创建⼀个 第三⽅合约 ,将次⾼价⾦额、买家、卖家、仲裁⼈信息转⼊到第三⽅合约。 ii. 转差额(最⾼价-次⾼价的)给买家。

  8. 对第三⽅合约投票

  9. 合约中含有买家,卖家,仲裁⼈,次⾼价⾦额。

  10. 仅这三个⼈可以进⾏投票,且每⼈⼀票。

  11. 票数达到两票的⼈获得合约中的⾦额,拍卖结束

1.定义商品结构

 struct Product {
        //
        uint id; //商品唯一id
        string name;//商品名称
        string category;//商品类别
        string imageLink;//ipfs-->图片链接
        string descLink;//ipfs-->商品描述信息链接

        uint startPrice;//竞拍起始价格
        uint auctionStartTime;//竞拍开始时间
        uint auctionEndTime;//竞拍结束时间

        ProductStatus status; 
        ProductCondition condition;
     
             //竞标信息
        uint highestBid;   //最高出价, 50,理想价格
        address highestBidder; //最高出价人
        uint secondHighestBid; //次高价,40
        uint totalBids; //所有的竞标数量  
    }
	enum ProductStatus {Open, Sold, Unsold} //竞标中,卖了,没卖出
    enum ProductCondition {Used, New} 新的,旧的

2.存储商品结构

mapping(卖家地址address => mapping(产品id => 产品Product)) stores
mapping(产品id => 卖家地址address) productToOwner

3.添加商品函数

  function addProductToStore(string _name, string _category, string _imageLink, string _descLink, uint _startTime, uint _endTime, uint _startPrice, uint condition) public {
        productIndex++; //全局唯一
        
        Product memory product = Product({
            id : productIndex,
            name : _name,
            category : _category,
            imageLink : _imageLink,
            descLink : _descLink,
            
            startPrice : _startPrice,
            auctionStartTime : _startTime,
            auctionEndTime : _endTime,
    
            status : ProductStatus.Open,
            condition : ProductCondition(condition)
            
            highestBid: 0,
            highestBidder : 0,
            secondHighestBid : 0,
            totalBids : 0   
            });
            
        //把创建好的商品添加到我们的商店结构中
        stores[msg.sender][productIndex] = product;
        productIdToOwner[productIndex] = msg.sender;
    }
}

4.通过商品id获取商品信息

  function getProductById(uint _productId) public view returns (uint, string, string, string, string, uint, uint, uint, uint){
        
      //通过id向定义的两个mapping结构中查询并返回
        address owner = productIdToOwner[_productId];
        Product memory product = stores[owner][_productId];
        
        return (
            product.id, product.name, product.category, product.imageLink, product.descLink,
            product.auctionStartTime, product.auctionEndTime, product.startPrice, uint(product.status)
        );

5.定义竞标结构

 	 struct Bid {
        uint productId;//产品id
        uint price; //迷惑价格,不是理想价格
        bool isRevealed;//是否揭标
        address bidder;//竞标人
    }

6.在Product中增加存储所有合约自己相关竞标的结构

        // 存储所有的竞标人对这个商品的竞标(与stroes类似)
        mapping(address => mapping(bytes32 => Bid)) bids;

7.添加竞标函数bid

    function bid(uint _productId, uint _idealPrice, string _secret) public payable {
        //将传入的理想价格和明文进行keccak256加密成密文
        bytes memory bytesInfo = abi.encodePacked(_idealPrice, _secret);
        bytes32 bytesHash = keccak256(bytesInfo);
        
        //通过id获取商品信息
        address owner = productIdToOwner[_productId];
        Product storage product = stores[owner][_productId];
        
        //每一个竞标必须大于等于起始价格
        require(msg.value >= product.startPrice);
        
        //竞标总数+1
        product.totalBids++;
        
        //存储竞标人对这个商品的竞标
        Bid memory bidLocal = Bid(_productId, msg.value, false, msg.sender);
        product.bids[msg.sender][bytesHash] = bidLocal; 
    }

8.验证竞标

    //获取指定的bid信息
    function getBidById(uint _productId, uint _idealPrice, string _secret) public view returns (uint, uint, bool, address) {
        
        //通过id获取商品信息
        address owner = productIdToOwner[_productId];
        Product storage product = stores[owner][_productId];
        
        //通过参数生成密文
        bytes memory bytesInfo = abi.encodePacked(_idealPrice, _secret);
        bytes32 bytesHash = keccak256(bytesInfo);
		
        //从竞标mapping取出竞标信息并返回
        Bid memory bidLocal = product.bids[msg.sender][bytesHash];
        return (bidLocal.productId, bidLocal.price, bidLocal.isRevealed, bidLocal.bidder);
    }

9.揭标

    //揭标事件,做log日志返回给客户端
    event revealEvent(uint productid, bytes32 bidId, uint idealPrice, uint price, uint refund);
    
    function revealBid(uint _productId, uint _idealPrice, string _secret) public {
       
        //通过id取商品信息
        address owner = productIdToOwner[_productId];
        Product storage product = stores[owner][_productId];
        
        //生成密文
        bytes memory bytesInfo = abi.encodePacked(_idealPrice, _secret);
        bytes32 bidId = keccak256(bytesInfo);
        
        //mapping(address => mapping(bytes32 => Bid)) bids;
        
        //一个人可以对同一个商品竞标多次,揭标的时候也要揭标多次, storage类型
        //获取当前id的bid信息
        Bid storage currBid = product.bids[msg.sender][bidId];
        
        //当前时间要大于竞拍时间,测试的时候可以关闭
        require(now > product.auctionStartTime);
        
       //每个标只能揭标一次,判断是否揭过
        require(!currBid.isRevealed);
        
        //说明找到了这个标, 找到了正确的标
        require(currBid.bidder != address(0));  

        currBid.isRevealed = true;

        //bid中的是迷惑价格,真实价格揭标时传递进来
        uint confusePrice = currBid.price;

        //退款金额, 程序最后,统一退款
        uint refund = 0;
        
        //理想价格
        uint idealPrice = _idealPrice;
        
        //迷惑价格小于理想价格
        if (confusePrice < idealPrice) {
            //路径1:无效交易,退还金额
            refund = confusePrice;
        } else {
            //理想价格大于最高价
            if (idealPrice > product.highestBid) {
                if (product.highestBidder == address(0)) {
                    //当前账户是第一个揭标人
                    //路径2:
                    product.highestBidder = msg.sender;
                    product.highestBid = idealPrice;
                    product.secondHighestBid = product.startPrice;
                    refund = confusePrice - idealPrice;
                } else {
                    //路径3:不是第一个,但是出价是目前最高的,更新最高竞标人,最高价格,次高价格
                    product.highestBidder.transfer(product.highestBid);
                    product.secondHighestBid = product.highestBid;
                    
                    product.highestBid = idealPrice; //wangwu 40
                    product.highestBidder = msg.sender; //wangwu
                    refund = confusePrice - idealPrice; //10
                }
            } else {
                //路径4:价格低于最高价,但是高于次高价
                if (idealPrice > product.secondHighestBid) {
                    //路径4:更新次高价,然后拿回自己的钱
                    product.secondHighestBid = idealPrice;
                    refund = confusePrice; //40
                
                } else {
                    //路径5:路人甲,价格低于次高价,直接退款
                    refund = confusePrice;
                }
            }
        }
	//提交事件
        emit revealEvent(_productId, bidId, confusePrice, currBid.price, refund);
		//退款
        if (refund > 0) {
            msg.sender.transfer(refund);
        }
    }
    

10.终结函数

    //key是产品id,value:是第三方合约
    //全局唯一, 用于投票时,找到这个第三方合约。
	mapping(uint => address) public productToEscrow;
    
    function finalaizeAuction(uint _productId) public {
        
        //获取商品信息
        address owner = productIdToOwner[_productId];
        Product storage product = stores[owner][_productId];
        
        address buyer = product.highestBidder; //买家
        address seller = owner;//卖家
        address arbiter = msg.sender; //仲裁人
        
        //仲裁人不允许是买家或者卖家
        require(arbiter != buyer && arbiter != seller);
        
        //限定仅在揭标之后才可以进行仲裁
        //require(now > product.auctionEndTime);

        require(product.status == ProductStatus.Open); //Open, Sold, Unsold

        //如果竞标了,但是没有揭标,那么也是没有卖出去(自行拓展)
        if (product.totalBids == 0) {
            product.status = ProductStatus.Unsold;
        } else {
            product.status = ProductStatus.Sold;
        }
        
		//.value()方式进行外部调用时转钱
        //类比feed.info.value(10).gas(800)(); 
        //这是构造的时候传钱,constructor加上payable关键字
        
	    //address escrow = (new Escrow).value(25)(buyer, seller, arbiter)
        address escrow = (new Escrow).value(product.secondHighestBid)(buyer, seller, arbiter);
        
        productToEscrow[_productId] = escrow;
        
        //退还差价 30- 25 = 5 , 30是理想出价,25是次高
        buyer.transfer(product.highestBid - product.secondHighestBid);
    }

11.三方合约(另起合约)

contract Escrow {

    // 属性:
    // 1. 买家
    address public buyer;
    // 2. 卖家
    address public seller;
    // 3. 仲裁人
    address public arbiter;

    //构造函数
    constructor(address _buyer, address _seller, address _arbiter) public payable {
        buyer = _buyer;
        seller = _seller;
        arbiter = _arbiter;
    }
    
    function getBalance() public view returns(uint256) {
        return address(this).balance;
    }
}

12.在Escrow中实现两个投票函数

    // 4. 卖家获得的票数
    uint sellerVotesCount;
    // 5. 买家获得的票数
    uint buyerVotesCount;
    
    // 6. 标记某个地址是否已经投票
    mapping(address => bool) addressVotedMap;

    // 7. 是否已经完成付款了
    bool isSpent = false;   

	// 方法:
    //向卖家投票方法
    function giveMoneyToSeller(address caller)  callerRestrict(caller) public {
        require(!isSpent);
        
        //记录已经投票的状态,如果投过票,就设置为true
        //不是mapping中的成员不可以投
        require(!addressVotedMap[caller]);
        addressVotedMap[caller] = true; //address => bool
        
        //sellerVotesCount++;
        if (++sellerVotesCount == 2 ) {
            isSpent = true;
            //转钱
            seller.transfer(address(this).balance);
        }
    }

    //向买家投票方法
    function giveMoneyToBuyer(address caller) callerRestrict(caller) public {
        require(!isSpent);
        require(!addressVotedMap[caller]);
        addressVotedMap[caller] = true;

        if (++buyerVotesCount == 2) {
            isSpent = true;
            buyer.transfer(address(this).balance);
        }
    }
    

13.添加修饰器

    modifier callerRestrict(address caller ) {
        require(caller == seller || caller == buyer || caller == arbiter);
        _;
    }//必须是三方中一员才可以投票

14.获取仲裁信息

    function escrowInfo() public view returns(address, address, address, uint, uint, uint256) {
        //获取合约余额
        uint256 balance = getEscrowBalance();
        return (buyer, seller, arbiter, buyerVotesCount, sellerVotesCount, balance);
    }
    
    
    function getEscrowBalance() public view returns(uint256) {
        return address(this).balance;
    }

15.更新EcommerceStore,对投票合约进行调用

    function getEscrowInfo(uint _productId) public view returns (address, address, address, uint, uint, uint256) {
        //获取仲裁商品对应的地址
        address escrow = productToEscrow[_productId];
        Escrow instanceContract = Escrow(escrow);
        
        return instanceContract.escrowInfo();
    }

    function giveToSeller(uint _productId) public {
        address contract1 = productToEscrow[_productId];
        Escrow(contract1).giveMoneyToSeller(msg.sender); //把调用人传给Escrow合约
    }

    function giveToBuyer(uint _productId) public {
        Escrow(productToEscrow[_productId]).giveMoneyToBuyer(msg.sender);
    }

16.完整的Escrow合约



contract Escrow {

    // 属性:
    // 1. 买家
    address  buyer;
    // 2. 卖家
    address  seller;
    // 3. 仲裁人
    address  arbiter;
    
    
    // 4. 卖家获得的票数
    uint sellerVotesCount;
    // 5. 买家获得的票数
    uint buyerVotesCount;
    
    // 6. 标记某个地址是否已经投票
    mapping(address => bool) addressVotedMap;

    // 7. 是否已经完成付款了
    bool isSpent = false;
    

    constructor(address _buyer, address _seller, address _arbiter) public payable {
        buyer = _buyer;
        seller = _seller;
        arbiter = _arbiter;
    }
    
        // 方法:
    //向卖家投票方法
    function giveMoneyToSeller(address caller)  callerRestrict(caller) public {
        require(!isSpent);
        
        //记录已经投票的状态,如果投过票,就设置为true
        require(!addressVotedMap[caller]);
        addressVotedMap[caller] = true; //address => bool
        
        //sellerVotesCount++;
        if (++sellerVotesCount == 2 ) {
            isSpent = true;
            seller.transfer(address(this).balance);
        }
    }

    //向买家投票方法
    function giveMoneyToBuyer(address caller) callerRestrict(caller) public {
        require(!isSpent);
        require(!addressVotedMap[caller]);
        addressVotedMap[caller] = true;

        if (++buyerVotesCount == 2) {
            isSpent = true;
            buyer.transfer(address(this).balance);
        }
    }
    
    
    function escrowInfo() public view returns(address, address, address, uint, uint, uint256) {
        uint256 balance = getEscrowBalance();
        return (buyer, seller, arbiter, buyerVotesCount, sellerVotesCount, balance);
    }
    
    
    
    modifier callerRestrict(address caller ) {
        require(caller == seller || caller == buyer || caller == arbiter);
        _;
    }
    
    
    function getEscrowBalance() public view returns(uint256) {
        return address(this).balance;
    }
}

17.完整的EcommerceStore合约

pragma solidity ^0.4.24;


contract EcommerceStore {
    
    //把商品添加到商店的业务分析:
    
    // 1. 定义商品结构
    // 2. 每个商品有一个唯一的id,数字,每添加一个商品,id++
    // 3. 需要有一个存储所有商品的结构, 通过id可以的到对应的商品
    // 4. 添加商品的方法
    
    uint public productIndex;  //每一个产品都有自己的id
    
    struct Product {
        //基础信息
        uint id;
        string name;
        string category;
        string imageLink;
        string descLink;

        uint startPrice;
        uint auctionStartTime;
        uint auctionEndTime;

        ProductStatus status; 
        ProductCondition condition;
        
        
        //竞标信息
        uint highestBid;   //最高出价, 50,理想价格
        address highestBidder; //最高出价人
        uint secondHighestBid; //次高价,40
        uint totalBids; //所有的竞标数量
        
        // 存储所有的竞标人对这个商品的竞标(与stroes类似)
        mapping(address => mapping(bytes32 => Bid)) bids;
        
    }

    enum ProductStatus {Open, Sold, Unsold} //竞标中,卖了,没卖出
    enum ProductCondition {Used, New} 新的,旧的
    
    
    mapping(address => mapping(uint => Product)) stores;
    mapping(uint => address) productIdToOwner;
    
    
    function addProductToStore(string _name, string _category, string _imageLink, string _descLink, uint _startTime, uint _endTime, uint _startPrice, uint condition) public {
        productIndex++;
        
        Product memory product = Product({
            id : productIndex,
            name : _name,
            category : _category,
            imageLink : _imageLink,
            descLink : _descLink,
            
            startPrice : _startPrice,
            auctionStartTime : _startTime,
            auctionEndTime : _endTime,
    
            status : ProductStatus.Open,
            condition : ProductCondition(condition),
            
            //++++++++++++
            highestBid: 0,
            highestBidder : 0,
            secondHighestBid : 0,
            totalBids : 0
            
            });
            
        //把创建好的商品添加到我们的商店结构中
        stores[msg.sender][productIndex] = product;
        productIdToOwner[productIndex] = msg.sender;
    }
    
    
    function getProductById(uint _productId) public view returns (uint, string, string, string, string, uint, uint, uint, uint){
        
        address owner = productIdToOwner[_productId];
        Product memory product = stores[owner][_productId];
        
        return (
            product.id, product.name, product.category, product.imageLink, product.descLink,
            product.auctionStartTime, product.auctionEndTime, product.startPrice, uint(product.status)
        );
    }
    
    
    // 竞标的结构:
    // 	1. 产品ID
    // 	2. 转账(迷惑)价格,注意,不是理想价格
    // 	3. 揭标与否
    // 	4. 竞标人

    struct Bid {
        uint productId;
        uint price;
        bool isRevealed;
        address bidder;
    }

    
    function bid(uint _productId, uint _idealPrice, string _secret) public payable {
        
        bytes memory bytesInfo = abi.encodePacked(_idealPrice, _secret);
        bytes32 bytesHash = keccak256(bytesInfo);
        
        address owner = productIdToOwner[_productId];
        Product storage product = stores[owner][_productId];
        
        //每一个竞标必须大于等于起始价格
        require(msg.value >= product.startPrice);
        
        product.totalBids++;
        
        Bid memory bidLocal = Bid(_productId, msg.value, false, msg.sender);
        product.bids[msg.sender][bytesHash] = bidLocal; 
    }
    
    //获取指定的bid信息
    function getBidById(uint _productId, uint _idealPrice, string _secret) public view returns (uint, uint, bool, address) {
       
        //Product storage product = stores[productIdToOwner[_productId]][_productId];
        address owner = productIdToOwner[_productId];
        Product storage product = stores[owner][_productId];
        
        bytes memory bytesInfo = abi.encodePacked(_idealPrice, _secret);
        bytes32 bytesHash = keccak256(bytesInfo);

        Bid memory bidLocal = product.bids[msg.sender][bytesHash];
        return (bidLocal.productId, bidLocal.price, bidLocal.isRevealed, bidLocal.bidder);
    }
    
    function getBalance() public view returns (uint){
        return address(this).balance;
    }
    
    
    event revealEvent(uint productid, bytes32 bidId, uint idealPrice, uint price, uint refund);
    
    function revealBid(uint _productId, uint _idealPrice, string _secret) public {
       
        address owner = productIdToOwner[_productId];
        Product storage product = stores[owner][_productId];
        
        bytes memory bytesInfo = abi.encodePacked(_idealPrice, _secret);
        bytes32 bidId = keccak256(bytesInfo);
        
        //mapping(address => mapping(bytes32 => Bid)) bids;
        
        //一个人可以对同一个商品竞标多次,揭标的时候也要揭标多次, storage类型
        Bid storage currBid = product.bids[msg.sender][bidId];
        
       //require(now > product.auctionStartTime);
       //每个标只能揭标一次
        require(!currBid.isRevealed);
        
        require(currBid.bidder != address(0));  //说明找到了这个标, 找到了正确的标

        currBid.isRevealed = true;

        //bid中的是迷惑价格,真实价格揭标时传递进来
        uint confusePrice = currBid.price;

        //退款金额, 程序最后,统一退款
        uint refund = 0;
        
        uint idealPrice = _idealPrice;
        
        if (confusePrice < idealPrice) {
            //路径1:无效交易
            refund = confusePrice;
        } else {
            if (idealPrice > product.highestBid) {
                if (product.highestBidder == address(0)) {
                    //当前账户是第一个揭标人
                    //路径2:
                    product.highestBidder = msg.sender;
                    product.highestBid = idealPrice;
                    product.secondHighestBid = product.startPrice;
                    refund = confusePrice - idealPrice;
                } else {
                    //路径3:不是第一个,但是出价是目前最高的,更新最高竞标人,最高价格,次高价格
                    product.highestBidder.transfer(product.highestBid);
                    product.secondHighestBid = product.highestBid;
                    
                    product.highestBid = idealPrice; //wangwu 40
                    product.highestBidder = msg.sender; //wangwu
                    refund = confusePrice - idealPrice; //10
                }
            } else {
                //路径4:价格低于最高价,但是高于次高价
                if (idealPrice > product.secondHighestBid) {
                    //路径4:更新次高价,然后拿回自己的钱
                    product.secondHighestBid = idealPrice;
                    refund = confusePrice; //40
                
                } else {
                    //路径5:路人甲,价格低于次高价,直接退款
                    refund = confusePrice;
                }
            }
        }

        emit revealEvent(_productId, bidId, confusePrice, currBid.price, refund);

        if (refund > 0) {
            msg.sender.transfer(refund);
        }
    }
    
    
    function getHighestBidInfo(uint _productId) public view returns(address, uint, uint, uint) {
        address owner = productIdToOwner[_productId];
        Product memory product = stores[owner][_productId];
        
        return (product.highestBidder, product.highestBid, product.secondHighestBid, product.totalBids);
    }
    
    
    //key是产品id,value:是第三方合约
    //全局唯一, 用于投票时,找到这个第三方合约。
	mapping(uint => address) public productToEscrow;
    
    function finalaizeAuction(uint _productId) public {
        
        address owner = productIdToOwner[_productId];
        Product storage product = stores[owner][_productId];
        
        address buyer = product.highestBidder; //买家
        address seller = owner;//卖家
        address arbiter = msg.sender; //仲裁人
        
        //仲裁人不允许是买家或者卖家
        require(arbiter != buyer && arbiter != seller);
        
        //限定仅在揭标之后才可以进行仲裁
        //require(now > product.auctionEndTime);

        require(product.status == ProductStatus.Open); //Open, Sold, Unsold

        //如果竞标了,但是没有揭标,那么也是没有卖出去(自行拓展)
        if (product.totalBids == 0) {
            product.status = ProductStatus.Unsold;
        } else {
            product.status = ProductStatus.Sold;
        }
        
		//.value()方式进行外部调用时转钱
        //类比feed.info.value(10).gas(800)(); 
        //这是构造的时候传钱,constructor加上payable关键字
        
	    //address escrow = (new Escrow).value(25)(buyer, seller, arbiter)
        address escrow = (new Escrow).value(product.secondHighestBid)(buyer, seller, arbiter);
        
        productToEscrow[_productId] = escrow;
        
        //退还差价 30- 25 = 5 , 30是理想出价,25是次高
        buyer.transfer(product.highestBid - product.secondHighestBid);
    }
    
    
    function getEscrowInfo(uint _productId) public view returns (address, address, address, uint, uint, uint256) {
        address escrow = productToEscrow[_productId];
        Escrow instanceContract = Escrow(escrow);
        
        return instanceContract.escrowInfo();
    }

    function giveToSeller(uint _productId) public {
        address contract1 = productToEscrow[_productId];
        Escrow(contract1).giveMoneyToSeller(msg.sender); //把调用人传给Escrow合约
    }

    function giveToBuyer(uint _productId) public {
        Escrow(productToEscrow[_productId]).giveMoneyToBuyer(msg.sender);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值