1 合约说明
1.1 功能介绍
- 去中心化自由职业市场是一个基于
SUI
区块链的智能合约平台,使雇主和自由职业者能够进行无需信任的交易。该平台利用Move语言开发智能合约,提供创建自由职业项目(freelance gigs
)、处理付款、争议解决和各种其他功能。 - 但该合约功能并不完整且本身编码和逻辑问题较多,仅对合约做了编码层面的修复,使其可以成功编译和部署。修改点:https://github.com/bityoume/dacade_decentralized_freelance_marketplace/commit/36d92cbc8bd646da8229fc4307cde14dbc5d55f1
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
- 命令输出关键信息截图
- 将关键的对象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
# PACKAGE_ID::freelance_marketplace::FreelanceGig
export GIG=0x4a423df0135adadb67dcd04a7fe8f4680e972b1a98b2b5197a87873d4bb014b3
- 查看工作对象
sui client object $GIG
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
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
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
4.5 支付薪酬(release_payment
)
切换Jason
sui client call --function release_payment --package $PACKAGE_ID --module freelance_marketplace --args $GIG --gas-budget 10000000
- 雇员获得薪资对象
sui client object 0xaa336e6e334debd8282b3f460f47c18da7bd69d78ded5c88acb33e749b583d28
- 查看工作对象
工作对象已经被重置。
评论前必须登录!
注册