forked from MystenLabs/sui
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[move] new fungible token example: synthetic currency backed by baske…
…t of other currencies Useful to show how to represent a "reserve" via a singleton shared object. A variant of this approach can also be used to issue a token that is backed by a basket of NFT's.
- Loading branch information
1 parent
3d6c70a
commit 0ccce8e
Showing
4 changed files
with
139 additions
and
3 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
# Fungible Tokens | ||
|
||
* MANAGED: a token managed by a treasurer trusted for minting and burning. This is how (e.g.) a fiat-backed stablecoin would work. | ||
* BASKET: a synthetic token backed by a basket of other assets. This how (e.g.) a [SDR](https://www.imf.org/en/About/Factsheets/Sheets/2016/08/01/14/51/Special-Drawing-Right-SDR)-like asset would work. | ||
* FIXED: a token with a fixed supply (coming soon). | ||
* ALGO: a token with an algorithmic issuance policy (coming soon). |
92 changes: 92 additions & 0 deletions
92
sui_programmability/examples/fungible_tokens/sources/BASKET.move
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,92 @@ | ||
// Copyright (c) 2022, Mysten Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
/// A synthetic fungible token backed by a basket of other tokens. | ||
/// Here, we use a basket that is 1:1 SUI and MANAGED, | ||
/// but this approach would work for a basket with arbitrary assets/ratios. | ||
/// E.g., [SDR](https://www.imf.org/en/About/Factsheets/Sheets/2016/08/01/14/51/Special-Drawing-Right-SDR) | ||
/// could be implemented this way. | ||
module FungibleTokens::BASKET { | ||
use FungibleTokens::MANAGED::MANAGED; | ||
use Sui::Coin::{Self, Coin, TreasuryCap}; | ||
use Sui::ID::VersionedID; | ||
use Sui::SUI::SUI; | ||
use Sui::Transfer; | ||
use Sui::TxContext::{Self, TxContext}; | ||
|
||
/// Name of the coin. By convention, this type has the same name as its parent module | ||
/// and has no fields. The full type of the coin defined by this module will be `COIN<BASKET>`. | ||
struct BASKET has drop { } | ||
|
||
/// Singleton shared object holding the reserve assets and the capability. | ||
struct Reserve has key { | ||
id: VersionedID, | ||
/// capability allowing the reserve to mint and burn BASKET | ||
treasury_cap: TreasuryCap<BASKET>, | ||
/// SUI coins held in the reserve | ||
sui: Coin<SUI>, | ||
/// MANAGED coins held in the reserve | ||
managed: Coin<MANAGED>, | ||
} | ||
|
||
/// Needed to deposit a 1:1 ratio of SUI and MANAGED for minting, but deposited a different ratio | ||
const EBAD_DEPOSIT_RATIO: u64 = 0; | ||
|
||
fun init(ctx: &mut TxContext) { | ||
// Get a treasury cap for the coin put it in the reserve | ||
let treasury_cap = Coin::create_currency<BASKET>(BASKET{}, ctx); | ||
Transfer::share_object(Reserve { | ||
id: TxContext::new_id(ctx), | ||
treasury_cap, | ||
sui: Coin::zero<SUI>(ctx), | ||
managed: Coin::zero<MANAGED>(ctx), | ||
}) | ||
} | ||
|
||
/// === Writes === | ||
/// Mint BASKET coins by accepting an equal number of SUI and MANAGED coins | ||
public fun mint( | ||
reserve: &mut Reserve, sui: Coin<SUI>, managed: Coin<MANAGED>, ctx: &mut TxContext | ||
): Coin<BASKET> { | ||
let num_sui = Coin::value(&sui); | ||
assert!(num_sui == Coin::value(&managed), EBAD_DEPOSIT_RATIO); | ||
|
||
Coin::join(&mut reserve.sui, sui); | ||
Coin::join(&mut reserve.managed, managed); | ||
Coin::mint(num_sui, &mut reserve.treasury_cap, ctx) | ||
} | ||
|
||
/// Burn BASKET coins and return the underlying reserve assets | ||
public fun burn( | ||
reserve: &mut Reserve, basket: Coin<BASKET>, ctx: &mut TxContext | ||
): (Coin<SUI>, Coin<MANAGED>) { | ||
let num_basket = Coin::value(&basket); | ||
Coin::burn(basket, &mut reserve.treasury_cap); | ||
let sui = Coin::withdraw(&mut reserve.sui, num_basket, ctx); | ||
let managed = Coin::withdraw(&mut reserve.managed, num_basket, ctx); | ||
(sui, managed) | ||
} | ||
|
||
// === Reads === | ||
|
||
/// Return the number of `MANAGED` coins in circulation | ||
public fun total_supply(reserve: &Reserve): u64 { | ||
Coin::total_supply(&reserve.treasury_cap) | ||
} | ||
|
||
/// Return the number of SUI in the reserve | ||
public fun sui_supply(reserve: &Reserve): u64 { | ||
Coin::value(&reserve.sui) | ||
} | ||
|
||
/// Return the number of MANAGED in the reserve | ||
public fun managed_supply(reserve: &Reserve): u64 { | ||
Coin::value(&reserve.managed) | ||
} | ||
|
||
#[test_only] | ||
public fun init_for_testing(ctx: &mut TxContext) { | ||
init(ctx) | ||
} | ||
} |
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
44 changes: 44 additions & 0 deletions
44
sui_programmability/examples/fungible_tokens/tests/BASKETTests.move
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,44 @@ | ||
// Copyright (c) 2022, Mysten Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#[test_only] | ||
module FungibleTokens::BASKETTests { | ||
use FungibleTokens::BASKET::{Self, Reserve}; | ||
use FungibleTokens::MANAGED::MANAGED; | ||
use Sui::Coin; | ||
use Sui::SUI::SUI; | ||
use Sui::TestScenario; | ||
|
||
#[test] | ||
public fun test_mint_burn() { | ||
let user = @0xA; | ||
|
||
let scenario = &mut TestScenario::begin(&user); | ||
{ | ||
let ctx = TestScenario::ctx(scenario); | ||
BASKET::init_for_testing(ctx); | ||
}; | ||
TestScenario::next_tx(scenario, &user); | ||
{ | ||
let reserve = TestScenario::remove_object<Reserve>(scenario); | ||
let ctx = TestScenario::ctx(scenario); | ||
assert!(BASKET::total_supply(&reserve) == 0, 0); | ||
|
||
let num_coins = 10; | ||
let sui = Coin::mint_for_testing<SUI>(num_coins, ctx); | ||
let managed = Coin::mint_for_testing<MANAGED>(num_coins, ctx); | ||
let basket = BASKET::mint(&mut reserve, sui, managed, ctx); | ||
assert!(Coin::value(&basket) == num_coins, 1); | ||
assert!(BASKET::total_supply(&reserve) == num_coins, 2); | ||
|
||
let (sui, managed) = BASKET::burn(&mut reserve, basket, ctx); | ||
assert!(Coin::value(&sui) == num_coins, 3); | ||
assert!(Coin::value(&managed) == num_coins, 4); | ||
|
||
Coin::keep(sui, ctx); | ||
Coin::keep(managed, ctx); | ||
TestScenario::return_object(scenario, reserve); | ||
} | ||
} | ||
|
||
} |