Dacade平台SUI Move挑战者合约实践——去中心化自由职业市场(Decentralized Freelance Marketplace)

  • rzexin
  • 更新于 2024-02-25 15:51
  • 阅读 1667

Dacade平台SUI Move挑战者合约实践——去中心化自由职业市场(Decentralized Freelance Marketplace)

Dacade平台SUI Move挑战者合约实践——去中心化自由职业市场(Decentralized Freelance Marketplace)

rzexin 2024.02.24

1 合约说明

1.1 功能介绍

1.2 合约代码

1.2.1 合约源码地址

  • 挑战提交者代码

https://github.com/Tevin-Isaac/sui-marketplace-move/blob/master/sources/marketplace.move

  • 编译错误修复代码

后续贴出的代码,均使用修复后的代码。

https://github.com/bityoume/dacade_decentralized_freelance_marketplace/blob/master/sources/marketplace.move

1.2.2 数据结构说明

(1)自由职业对象定义
  • 自由职业对象是一个共享对象,包括了临时工作的创建方、工作描述和支付薪水金额,以及应聘工作的自由职业者地址、是否完成工作和是否有争议等参数。
  • 参数有:
    • client:自由职业临时工作发布者(创建者)地址
    • freelancer:应聘临时工的自由职业者地址
    • description:自由职业工作内容描述
    • price:支付薪水金额
    • escrow:托管的SUI代币
    • workSubmitted:工作是否已完成并提交
    • dispute:是否有争议
      struct FreelanceGig has key, store {
      id: UID,
      client: address,
      freelancer: address,
      description: vector<u8>,
      price: u64,
      escrow: Balance<SUI>,
      workSubmitted: bool,
      dispute: bool,
      }

1.2.3 对外接口说明

(1)创建自由职业临时工作(create_gig
  • 雇主可以通过提供工作描述设定价格来创建自由职业临时工职位
  • 该自由职业临时工作对象(FreelanceGig)是一个共享对象
    public entry fun create_gig(description: vector<u8>, price: u64, ctx: &mut TxContext) {
        let gig_id = object::new(ctx);
        transfer::share_object(FreelanceGig {
            id: gig_id,
            client: tx_context::sender(ctx),
            freelancer: @0x0, // Set to an initial value, can be updated later
            description: description,
            price: price,
            escrow: balance::zero<SUI>(),
            workSubmitted: false,
            dispute: false,
        });
    }
(2)应聘临时工(bid_on_gig
  • 只要该临时工作未被竞标,任何人都可以进行申请

    注:一般雇主会发布他们需要完成的任务或项目,然后自由职业者可以对这些项目进行竞标,提出他们愿意接受这个工作以及他们的报价。雇主通常会根据报价、经验、技能和以往的工作表现等因素来选择最适合的自由职业者。本合约简化这块逻辑,是让最先的竞标者将会得到该工作,后续有用户再来竞标,将会直接报错。

    public entry fun bid_on_gig(gig: &mut FreelanceGig, ctx: &mut TxContext) {
        assert!(gig.freelancer == @0x0, EInvalidBid);
        gig.freelancer = tx_context::sender(ctx);
    }
(3)提交工作(submit_work
  • 当雇员完成工作,将会调用该接口提交工作,并标记该工作已经完成
    public entry fun submit_work(gig: &mut FreelanceGig, ctx: &mut TxContext) {
        assert!(gig.freelancer == tx_context::sender(ctx), EInvalidWork);
        gig.workSubmitted = true;
    }
(4)提交争议(dispute_gig)
  • 若雇员对工作有争议,将调用该接口进行提交争议
    public entry fun dispute_gig(gig_id: UID, ctx: &mut TxContext) {
        let gig = object::borrow_mut<FreelanceGig>(gig_id, ctx);
        assert!(gig.client == tx_context::sender(ctx), EDispute);
        gig.dispute = true;
    }
(5)解决争议(resolve_dispute
  • 如果雇员有争议,雇主可以调用该接口去进行解决
  • 若争议解决,将支付薪酬给雇员
  • 若争议未解决,将。。。!?
public entry fun resolve_dispute(gig: &mut FreelanceGig, resolved: bool, ctx: &mut TxContext) {
        assert!(gig.client == tx_context::sender(ctx), EDispute);
        assert!(!gig.dispute, EAlreadyResolved);

        let amount = balance::value(&gig.escrow);
        let take_coin = coin::take(&mut gig.escrow, amount, ctx);

        let recipient;
        if (resolved) {
            // Transfer funds to the freelancer
            recipient = gig.freelancer
        } else {
            // Refund funds to the client
            recipient = gig.client;
        };

        transfer::public_transfer(take_coin, recipient);

        // Reset gig state
        gig.freelancer = @0x0;
        gig.workSubmitted = false;
        gig.dispute = false;
    }
(6)支付薪酬(release_payment
  • 若雇员已经提交工作成果,且没有争议,雇主可以调用该接口支付薪酬
    public entry fun release_payment(gig: &mut FreelanceGig, ctx: &mut TxContext) {
        assert!(gig.client == tx_context::sender(ctx), ENotFreelancer);
        assert!(gig.workSubmitted && !gig.dispute, EInvalidWork);

        // Transfer funds to the freelancer
        let amount = balance::value(& gig.escrow);
        let take_coin = coin::take(&mut gig.escrow, amount, ctx);
        transfer::public_transfer(take_coin, gig.freelancer);

        // Reset gig state
        gig.freelancer = @0x0;
        gig.workSubmitted = false;
        gig.dispute = false;
    }
(7)取消工作(cancel_gig)
  • 工作发布者和应聘者都可以取消该工作
  • 若已经有应聘者,且工作没有提交也没有争议,托管资金将退还工作发布者
    public entry fun cancel_gig(gig: &mut FreelanceGig, ctx: &mut TxContext) {
        assert!(gig.client == tx_context::sender(ctx) || gig.freelancer == tx_context::sender(ctx), ENotFreelancer);

        // Refund funds to the client if not yet paid
        if (gig.freelancer != @0x0 && !gig.workSubmitted && !gig.dispute) {
            let amount = balance::value(&gig.escrow);
            let take_coin = coin::take(&mut gig.escrow, amount, ctx);
            transfer::public_transfer(take_coin, gig.client);
        };

        // Reset gig state
        gig.freelancer = @0x0;
        gig.workSubmitted = false;
        gig.dispute = false;
    }
(8)取回薪资(withdraw_earnings)
  • 应聘者可以取出薪资
    public entry fun withdraw_earnings(gig: &mut FreelanceGig, amount: u64, ctx: &mut TxContext) {
        // Withdraw earnings from the freelancer's balance
        let freelancer_id = object::new(ctx);
        let recipient = tx_context::sender(ctx);
        assert!(gig.freelancer == recipient, ENotFreelancer);

        let take_coin = coin::take(&mut gig.escrow, amount, ctx);
        let freelancer = Freelancer {
            id : freelancer_id,
            balance: coin::into_balance(take_coin),
        };

        transfer::public_transfer(freelancer, recipient);
    }

    public entry fun get_freelancer_balance(freelancer: &Freelancer): u64 {
        balance::value(&freelancer.balance)
    }
(9)添加托管薪水(add_funds_to_gig)
  • 雇主可以添加托管薪水
    public entry fun add_funds_to_gig(gig: &mut FreelanceGig, payment: Coin<SUI>, ctx: &mut TxContext) {
        assert!(tx_context::sender(ctx) == gig.client, ENotFreelancer);
        coin::put(&mut gig.escrow, payment);
    }
(10)取回托管薪水(request_refund)
  • 若雇员工作未提交,雇主可以取回托管薪水
    public entry fun request_refund(gig: &mut FreelanceGig, ctx: &mut TxContext) {
        assert!(tx_context::sender(ctx) == gig.client, ENotFreelancer);
        assert!(gig.workSubmitted == false, EInvalidWithdrawal);

        // Refund funds to the client
        let amount = balance::value(& gig.escrow);
        let take_coin = coin::take(&mut gig.escrow, amount, ctx);
        transfer::public_transfer(take_coin, gig.client);

        // Reset gig state
        gig.freelancer = @0x0;
        gig.workSubmitted = false;
        gig.dispute = false;
    }

2 前置准备

2.1 帐号准备及角色分配

别名 地址 角色
Jason 0x5c5882d73a6e5b6ea1743fb028eff5e0d7cc8b7ae123d27856c5fe666d91569a 雇主
Alice 0x2d178b9704706393d2630fe6cf9415c2c50b181e9e3c7a977237bb2929f82d19 雇员
Bob 0xf2e6ffef7d0543e258d4c47a53d6fa9872de4630cc186950accbd83415b009f0 雇员
  • 将地址添加到环境变量
export JASON=0x5c5882d73a6e5b6ea1743fb028eff5e0d7cc8b7ae123d27856c5fe666d91569a
export ALICE=0x2d178b9704706393d2630fe6cf9415c2c50b181e9e3c7a977237bb2929f82d19
export BOB=0xf2e6ffef7d0543e258d4c47a53d6fa9872de4630cc186950accbd83415b009f0

3 合约部署

切换到Jason账号

sui client publish --gas-budget 100000000
  • 命令输出关键信息截图

image.png

  • 将关键的对象ID记录到环境变量,方便后续调用使用
export PACKAGE_ID=0x4a57fbaf781579664efc618db090dd3d4818b811dd0fb3a91a9eb6403c371953

4 合约交互

4.1 创建自由职业临时工作(create_gig

切换到Jason

export DESC="test"
export PRICE=100

sui client call --function create_gig --package $PACKAGE_ID --module freelance_marketplace --args $DESC $PRICE --gas-budget 10000000
  • 记录自由职业对象ID

image.png

# PACKAGE_ID::freelance_marketplace::FreelanceGig
export GIG=0x4a423df0135adadb67dcd04a7fe8f4680e972b1a98b2b5197a87873d4bb014b3
  • 查看工作对象
sui client object $GIG

image.png

4.2 添加托管薪水(add_funds_to_gig)

export COIN=0x9dd8c0daf2bbdcd4efaf179cdef6e9383b80a1de2b7851f6293e8f721037d7cf # 800
sui client call --function add_funds_to_gig --package $PACKAGE_ID --module freelance_marketplace --args $GIG $COIN --gas-budget 10000000    
  • 查看工作对象
sui client object $GIG

image.png

4.3 应聘临时工(bid_on_gig

切换到Alice

若Alice已经应聘,Bob再次应聘的话,将会报错:

Error executing transaction: Failure {
    error: "MoveAbort(MoveLocation { module: ModuleId { address: 4a57fbaf781579664efc618db090dd3d4818b811dd0fb3a91a9eb6403c371953, name: Identifier(\"freelance_marketplace\") }, function: 3, instruction: 12, function_name: Some(\"bid_on_gig\") }, 1) in command 0",
}
sui client call --function bid_on_gig --package $PACKAGE_ID --module freelance_marketplace --args $GIG --gas-budget 10000000
  • 查看工作对象
sui client object $GIG

image.png

4.4 提交工作(submit_work

sui client call --function submit_work --package $PACKAGE_ID --module freelance_marketplace --args $GIG --gas-budget 10000000
  • 查看工作对象
sui client object $GIG

image.png

4.5 支付薪酬(release_payment

切换Jason

sui client call --function release_payment --package $PACKAGE_ID --module freelance_marketplace --args $GIG --gas-budget 10000000
  • 雇员获得薪资对象

image.png

sui client object 0xaa336e6e334debd8282b3f460f47c18da7bd69d78ded5c88acb33e749b583d28

image.png

  • 查看工作对象

工作对象已经被重置。

image.png

5 更多

🚀参与星航计划 开启你的 Sui Move 之旅!🚀

点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
rzexin
rzexin
0x6Fa5...8165
江湖只有他的大名,没有他的介绍。