Skip to content

Commit

Permalink
[move] Fix Escrow example and add tests (#937)
Browse files Browse the repository at this point in the history
  • Loading branch information
666lcz authored Mar 23, 2022
1 parent ea1c2ea commit 0e007eb
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 7 deletions.
20 changes: 13 additions & 7 deletions sui_programmability/examples/defi/sources/Escrow.move
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ module DeFi::Escrow {
escrowed: T,
}

// TODO: proper error codes
const ETODO: u64 = 0;
// Error codes
/// The `sender` and `recipient` of the two escrowed objects do not match
const EMISMATCHED_SENDER_RECIPIENT: u64 = 0;
/// The `exchange_for` fields of the two escrowed objects do not match
const EMISMATCHED_EXCHANGE_OBJECT: u64 = 1;

/// Create an escrow for exchanging goods with
/// `counterparty`, mediated by a `third_party`
Expand All @@ -49,7 +52,9 @@ module DeFi::Escrow {

/// Trusted third party can swap compatible objects
public fun swap<T1: key + store, T2: key + store>(
obj1: EscrowedObj<T1, T2>, obj2: EscrowedObj<T1, T2>
obj1: EscrowedObj<T1, T2>,
obj2: EscrowedObj<T2, T1>,
_ctx: &mut TxContext
) {
let EscrowedObj {
id: id1,
Expand All @@ -68,11 +73,11 @@ module DeFi::Escrow {
ID::delete(id1);
ID::delete(id2);
// check sender/recipient compatibility
assert!(&sender1 == &recipient2, ETODO);
assert!(&sender2 == &recipient1, ETODO);
assert!(&sender1 == &recipient2, EMISMATCHED_SENDER_RECIPIENT);
assert!(&sender2 == &recipient1, EMISMATCHED_SENDER_RECIPIENT);
// check object ID compatibility
assert!(ID::id(&escrowed1) == &exchange_for2, ETODO);
assert!(ID::id(&escrowed2) == &exchange_for1, ETODO);
assert!(ID::id(&escrowed1) == &exchange_for2, EMISMATCHED_EXCHANGE_OBJECT);
assert!(ID::id(&escrowed2) == &exchange_for1, EMISMATCHED_EXCHANGE_OBJECT);
// everything matches. do the swap!
Transfer::transfer(escrowed1, sender2);
Transfer::transfer(escrowed2, sender1)
Expand All @@ -81,6 +86,7 @@ module DeFi::Escrow {
/// Trusted third party can always return an escrowed object to its original owner
public fun return_to_sender<T: key + store, ExchangeForT: key + store>(
obj: EscrowedObj<T, ExchangeForT>,
_ctx: &mut TxContext
) {
let EscrowedObj {
id, sender, recipient: _, exchange_for: _, escrowed
Expand Down
159 changes: 159 additions & 0 deletions sui_programmability/examples/defi/tests/EscrowTests.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#[test_only]
module DeFi::EscrowTests {
use Sui::ID::{Self, VersionedID};
use Sui::TestScenario::{Self, Scenario};
use Sui::TxContext::{Self};

use DeFi::Escrow::{Self, EscrowedObj};

const ALICE_ADDRESS: address = @0xACE;
const BOB_ADDRESS: address = @0xACEB;
const THIRD_PARTY_ADDRESS: address = @0xFACE;
const RANDOM_ADDRESS: address = @123;

// Error codes.
const ESWAP_TRANSFER_FAILED: u64 = 0;
const ERETURN_TRANSFER_FAILED: u64 = 0;

// Example of an object type used for exchange
struct ItemA has key, store {
id: VersionedID
}

// Example of the other object type used for exchange
struct ItemB has key, store {
id: VersionedID
}

#[test]
public fun test_escrow_flow() {
// Both Alice and Bob send items to the third party
let scenario = &mut send_to_escrow(ALICE_ADDRESS, BOB_ADDRESS);
swap(scenario, &THIRD_PARTY_ADDRESS);

// Alice now owns item B, and Bob now owns item A
assert!(owns_object<ItemB>(scenario, &ALICE_ADDRESS), ESWAP_TRANSFER_FAILED);
assert!(owns_object<ItemA>(scenario, &BOB_ADDRESS), ESWAP_TRANSFER_FAILED);
}

#[test]
public fun test_return_to_sender() {
// Both Alice and Bob send items to the third party
let scenario = &mut send_to_escrow(ALICE_ADDRESS, BOB_ADDRESS);

// The third party returns item A to Alice, item B to Bob
TestScenario::next_tx(scenario, &THIRD_PARTY_ADDRESS);
{
let item_a = TestScenario::remove_object<EscrowedObj<ItemA, ItemB>>(scenario);
let ctx = TestScenario::ctx(scenario);
Escrow::return_to_sender<ItemA, ItemB>(item_a, ctx);

let item_b = TestScenario::remove_object<EscrowedObj<ItemB, ItemA>>(scenario);
let ctx = TestScenario::ctx(scenario);
Escrow::return_to_sender<ItemB, ItemA>(item_b, ctx);
};

// Alice now owns item A, and Bob now owns item B
assert!(owns_object<ItemA>(scenario, &ALICE_ADDRESS), ERETURN_TRANSFER_FAILED);
assert!(owns_object<ItemB>(scenario, &BOB_ADDRESS), ERETURN_TRANSFER_FAILED);
}

#[test]
#[expected_failure(abort_code = 1)]
public fun test_swap_wrong_objects() {
// Both Alice and Bob send items to the third party except that Alice wants to exchange
// for a different object than Bob's
let scenario = &mut send_to_escrow_with_overrides(ALICE_ADDRESS, BOB_ADDRESS, true, false);
swap(scenario, &THIRD_PARTY_ADDRESS);
}

#[test]
#[expected_failure(abort_code = 0)]
public fun test_swap_wrong_recipient() {
// Both Alice and Bob send items to the third party except that Alice put a different
// recipient than Bob
let scenario = &mut send_to_escrow_with_overrides(ALICE_ADDRESS, BOB_ADDRESS, false, true);
swap(scenario, &THIRD_PARTY_ADDRESS);
}

fun swap(scenario: &mut Scenario, third_party: &address) {
TestScenario::next_tx(scenario, third_party);
{
let item_a = TestScenario::remove_object<EscrowedObj<ItemA, ItemB>>(scenario);
let item_b = TestScenario::remove_object<EscrowedObj<ItemB, ItemA>>(scenario);
let ctx = TestScenario::ctx(scenario);
Escrow::swap(item_a, item_b, ctx);
};
}

fun send_to_escrow(
alice: address,
bob: address,
): Scenario {
send_to_escrow_with_overrides(alice, bob, false, false)
}

fun send_to_escrow_with_overrides(
alice: address,
bob: address,
override_exchange_for: bool,
override_recipient: bool,
): Scenario {
let new_scenario = TestScenario::begin(&alice);
let scenario = &mut new_scenario;
let ctx = TestScenario::ctx(scenario);
let item_a_versioned_id = TxContext::new_id(ctx);

TestScenario::next_tx(scenario, &bob);
let ctx = TestScenario::ctx(scenario);
let item_b_versioned_id = TxContext::new_id(ctx);

let item_a_id = *ID::inner(&item_a_versioned_id);
let item_b_id = *ID::inner(&item_b_versioned_id);
if (override_exchange_for) {
item_b_id = ID::new(RANDOM_ADDRESS);
};

// Alice sends item A to the third party
TestScenario::next_tx(scenario, &alice);
{
let ctx = TestScenario::ctx(scenario);
let escrowed = ItemA {
id: item_a_versioned_id
};
let recipient = bob;
if (override_recipient) {
recipient = RANDOM_ADDRESS;
};
Escrow::create<ItemA, ItemB>(
recipient,
THIRD_PARTY_ADDRESS,
item_b_id,
escrowed,
ctx
);
};

// Bob sends item B to the third party
TestScenario::next_tx(scenario, &BOB_ADDRESS);
{
let ctx = TestScenario::ctx(scenario);
let escrowed = ItemB {
id: item_b_versioned_id
};
Escrow::create<ItemB, ItemA>(
alice,
THIRD_PARTY_ADDRESS,
item_a_id,
escrowed,
ctx
);
};
new_scenario
}

fun owns_object<T: key + store>(scenario: &mut Scenario, owner: &address): bool{
TestScenario::next_tx(scenario, owner);
TestScenario::can_remove_object<T>(scenario)
}
}

1 comment on commit 0e007eb

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bench results

�[0m�[0m�[1m�[32m Finished�[0m release [optimized] target(s) in 0.29s
�[0m�[0m�[1m�[32m Running�[0m target/release/bench
�[2m2022-03-23T21:57:30.977776Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Starting benchmark: TransactionsAndCerts
�[2m2022-03-23T21:57:30.977813Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Preparing accounts.
�[2m2022-03-23T21:57:30.978272Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Open database on path: "/tmp/DB_930C667A700A2EA823FC3A8FC00D3DAFCC896B38"
�[2m2022-03-23T21:57:31.115341Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Init Authority.
�[2m2022-03-23T21:57:31.222038Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Generate empty store with Genesis.
�[2m2022-03-23T21:57:31.851565Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Preparing transactions.
�[2m2022-03-23T21:57:32.336589Z�[0m �[32m INFO�[0m �[2msui_network::transport�[0m�[2m:�[0m Listening to TCP traffic on 127.0.0.1:9555
�[2m2022-03-23T21:57:33.337859Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Number of TCP connections: 2
�[2m2022-03-23T21:57:33.337888Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Sending requests.
�[2m2022-03-23T21:57:33.338187Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m Sending TCP requests to 127.0.0.1:9555
�[2m2022-03-23T21:57:33.338311Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m Sending TCP requests to 127.0.0.1:9555
�[2m2022-03-23T21:57:35.550265Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m Done sending TCP requests to 127.0.0.1:9555
�[2m2022-03-23T21:57:35.581170Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m Done sending TCP requests to 127.0.0.1:9555
�[2m2022-03-23T21:57:35.581263Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Received 2000 responses.
�[2m2022-03-23T21:57:35.658810Z�[0m �[33m WARN�[0m �[2mbench�[0m�[2m:�[0m Completed benchmark for TransactionsAndCerts
Total time: 2243366us, items: 100000, tx/sec: 44575.87393229638

Please sign in to comment.