-
Notifications
You must be signed in to change notification settings - Fork 11.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Escrow app example to docs (#15812)
## Description Added the smart contract component to the Esrow app example --------- Co-authored-by: Daniel Lam <[email protected]> Co-authored-by: Ronny Roland <[email protected]>
- Loading branch information
1 parent
60fba35
commit 06749b1
Showing
9 changed files
with
1,659 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
--- | ||
title: Trading | ||
hide_table_of_contents: true | ||
--- | ||
|
||
Escrow refers to a legal concept where a third party holds and regulates payment of the funds required for two parties involved in a given transaction. It helps make transactions more secure by keeping the payment in a secure escrow account that is released only when all of the terms of an agreement are met as overseen by the party acting as the escrow authority. | ||
|
||
This guide and example demonstrate an atomic swap on Sui, which is similar to an escrow but does not require a trusted third party. Instead, this example uses a [shared object](../../../concepts/object-ownership/shared.mdx) to act as the escrow between two Sui users wanting to trade. Shared objects are a unique concept to Sui. Any transaction and any signer can modify it, given the changes meet the requirements set forth by the package that defined the type. | ||
|
||
:::info | ||
|
||
See [Shared versus Owned Objects](../sui-101/shared-owned.mdx) for more information on the differences between object types. | ||
|
||
::: | ||
|
||
The guide is split into three parts: | ||
|
||
1. [Backend](./trading/backend.mdx): The modules that hold the state and perform the swaps. | ||
1. [Indexing and API Service](./trading/indexer-api.mdx): A service that indexes chain state to discover trades, and an API service to read this data. | ||
1. [Frontend](./trading/frontend.mdx): Enables users to list objects for sale and to accept trades. | ||
|
||
{@include: ../../../snippets/app-examples-trading-source.mdx} | ||
|
||
## Prerequisites | ||
|
||
Before getting started, make sure you have: | ||
|
||
- [Installed the latest version of Sui](../getting-started/sui-install.mdx). | ||
- [Configured a valid network environment](../../../references/cli/client.mdx#set-current-environment), as you will be deploying the module on Testnet. |
160 changes: 160 additions & 0 deletions
160
docs/content/guides/developer/app-examples/trading/backend.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
--- | ||
title: Trading Backend | ||
sidebar_label: Backend | ||
--- | ||
|
||
:::note Multi-Page Guide | ||
|
||
This is the first in a [three-part guide](../trading.mdx) on how to build a trustless atomic swap on Sui. | ||
|
||
::: | ||
|
||
This particular protocol consists of three phases: | ||
|
||
1. One party `lock`s their object, obtaining a `Locked` object and its `Key`. This party can `unlock` their object to preserve liveness if the other party stalls before completing the second stage. | ||
2. The other party registers a publicly accessible, shared `Escrow` object. This effectively locks their object at a particular version as well, waiting for the first party to complete the swap. The second party is able to request their object is returned to them, to preserve liveness as well. | ||
3. The first party sends their locked object and its key to the shared `Escrow` object. This completes the swap, as long as all conditions are met: The sender of the swap transaction is the recipient of the `Escrow`, the key of the desired object (`exchange_key`) in the escrow matches the key supplied in the swap, and the key supplied in the swap unlocks the `Locked<U>`. | ||
|
||
Let's create a Sui project using the terminal command `sui move new escrow`. Create a file in the sources directory named `shared.move`, and let's go through the code together. | ||
|
||
## shared.move | ||
|
||
{@include: ../../../../snippets/app-examples-trading-source.mdx} | ||
|
||
Let's go through the code line by line: | ||
|
||
### Structs | ||
|
||
#### `Escrow<T>` | ||
|
||
This struct represents an object held in escrow. It contains the following fields: | ||
|
||
- `id`: Unique identifier for the escrow object. | ||
- `sender`: Address of the owner of the `escrowed` object. | ||
- `recipient`: Intended recipient of the `escrowed` object. | ||
- `exchange_key`: ID of the key that opens the lock on the object sender wants from the recipient. | ||
- `escrowed`: The actual object held in escrow. | ||
|
||
```move | ||
struct Escrow<T: key + store> has key, store { | ||
id: UID, | ||
sender: address, | ||
recipient: address, | ||
exchange_key: ID, | ||
escrowed: T, | ||
} | ||
``` | ||
|
||
The ID of the key is used as the `exchange_key`, rather than the ID of the object, to ensure that the object is not modified after a trade has been initiated. | ||
|
||
### Error codes | ||
|
||
Two constants are defined to represent potential errors during the execution of the swap: | ||
|
||
- `EMismatchedSenderRecipient`: The `sender` and `recipient` of the two escrowed objects do not match. | ||
- `EMismatchedExchangeObject`: The `exchange_for` fields of the two escrowed objects do not match. | ||
|
||
```move | ||
const EMismatchedSenderRecipient: u64 = 0; | ||
const EMismatchedExchangeObject: u64 = 1; | ||
``` | ||
|
||
### Public functions | ||
|
||
#### `create` | ||
|
||
This function is used to create a new escrow object. It takes four arguments: the object to be escrowed, the ID of the key that opens the lock on the object the sender wants from the recipient, the intended recipient, and the transaction context. | ||
|
||
```move | ||
public fun create<T: key + store>( | ||
escrowed: T, | ||
exchange_key: ID, | ||
recipient: address, | ||
ctx: &mut TxContext | ||
) { | ||
let escrow = Escrow { | ||
id: object::new(ctx), | ||
sender: tx_context::sender(ctx), | ||
recipient, | ||
exchange_key, | ||
escrowed, | ||
}; | ||
transfer::public_share_object(escrow); | ||
} | ||
``` | ||
|
||
#### `swap` | ||
|
||
This function is used to perform the swap operation. It takes four arguments: the escrow object, the key, the locked object, and the transaction context. | ||
|
||
```move | ||
public fun swap<T: key + store, U: key + store>( | ||
escrow: Escrow<T>, | ||
key: Key, | ||
locked: Locked<U>, | ||
ctx: &TxContext, | ||
): T { | ||
let Escrow { | ||
id, | ||
sender, | ||
recipient, | ||
exchange_key, | ||
escrowed, | ||
} = escrow; | ||
assert!(recipient == tx_context::sender(ctx), EMismatchedSenderRecipient); | ||
assert!(exchange_key == object::id(&key), EMismatchedExchangeObject); | ||
// Do the actual swap | ||
transfer::public_transfer(lock::unlock(locked, key), sender); | ||
object::delete(id); | ||
escrowed | ||
} | ||
``` | ||
|
||
The `object::delete` function call is used to delete the shared `Escrow` object. Previously, Move supported only the deletion of owned objects, but [shared-object deletion has since been enabled](https://github.com/MystenLabs/sui/pull/16008). | ||
|
||
#### `return_to_sender` | ||
|
||
This function is used to cancel the escrow and return the escrowed item to the sender. It takes two arguments: the escrow object and the transaction context. | ||
|
||
```move | ||
public fun return_to_sender<T: key + store>( | ||
escrow: Escrow<T>, | ||
ctx: &TxContext | ||
): T { | ||
let Escrow { | ||
id, | ||
sender, | ||
recipient: _, | ||
exchange_key: _, | ||
escrowed, | ||
} = escrow; | ||
assert!(sender == tx_context::sender(ctx), EMismatchedSenderRecipient); | ||
object::delete(id); | ||
escrowed | ||
} | ||
``` | ||
|
||
Once again, the shared `Escrow` object is deleted after the escrowed item is returned to the sender. | ||
|
||
### Tests | ||
|
||
The code includes several tests to ensure the correct functioning of the atomic swap process. These tests cover successful swaps, mismatches in sender or recipient, mismatches in the exchange object, tampering with the object, and returning the object to the sender. | ||
|
||
In conclusion, this code provides a robust and secure way to perform atomic swaps of objects in a decentralized system, without the need for a trusted third party. It uses shared objects and a series of checks to ensure that the swap only occurs if all conditions are met. | ||
|
||
## Deployment | ||
|
||
{@include: ../../../../snippets/initialize-sui-client-cli.mdx} | ||
|
||
{@include: ../../../../snippets/publish-to-devnet-with-coins.mdx} | ||
|
||
## Next steps | ||
|
||
You have written and deployed the Move package. To turn this into a complete dApp with frontend, you need to create a frontend. For the frontend to be updated, create an indexer that listens to the blockchain as escrows are made and swaps are fulfilled. | ||
|
||
For the next step, you [create the indexing service](./indexer-api.mdx). |
Oops, something went wrong.