Type-safe Move module that implements a fixed-rate tokenized vault with reserves in InputCoin and mint/burn of OutputCoin. The module exposes entry functions for vault creation, mint/redeem, and owner operations, with comprehensive overflow and invariant checks.
- Module:
vault::vault - Type parameters:
InputCoin,OutputCoin
-
struct Vault<phantom InputCoin, phantom OutputCoin> has key, storeid: UIDrate: u64— exchange multiplierreserve: Balance<InputCoin>— input reservesrate_decimals: u8— precision forrate
-
struct VaultMetadata<phantom InputCoin, phantom OutputCoin> has key, storeid: UIDname: string::Stringsymbol: ascii::Stringdescription: string::Stringicon_url: Option<Url>
-
struct OwnerCap has key, storeid: UIDvault_id: ID
-
create_vault<InputCoin, OutputCoin>(rate: u64, output_coin_treasury: TreasuryCap<OutputCoin>, rate_decimals: u8, symbol: vector<u8>, name: vector<u8>, description: vector<u8>, icon_url: Option<Url>, ctx: &mut TxContext)- Creates and shares the
Vault, creates and freezesVaultMetadata, linksTreasuryCap<OutputCoin>via dynamic fields, transfersOwnerCapto sender.
- Creates and shares the
-
deposit<InputCoin, OutputCoin>(owner_cap: &OwnerCap, vault: &mut Vault<...>, amount: Coin<InputCoin>)- Owner-only; joins
amountintoreserve.
- Owner-only; joins
-
withdraw<InputCoin, OutputCoin>(owner_cap: &OwnerCap, vault: &mut Vault<...>, amount: u64, ctx: &mut TxContext): Coin<InputCoin>- Owner-only; splits
reserveand returns input coins.
- Owner-only; splits
-
set_rate<InputCoin, OutputCoin>(owner_cap: &OwnerCap, vault: &mut Vault<...>, rate: u64)- Owner-only; updates
rate(must be > 0).
- Owner-only; updates
-
mint<InputCoin, OutputCoin>(vault: &mut Vault<...>, vault_metadata: &VaultMetadata<...>, input_coin: Coin<InputCoin>, ctx: &mut TxContext): Coin<OutputCoin>- Checks metadata binding, computes output via safe math, adds input to
reserve, mintsOutputCoinvia linkedTreasuryCap.
- Checks metadata binding, computes output via safe math, adds input to
-
redeem<InputCoin, OutputCoin>(vault: &mut Vault<...>, vault_metadata: &VaultMetadata<...>, output_coin: Coin<OutputCoin>, ctx: &mut TxContext): Coin<InputCoin>- Checks metadata binding, computes input via safe math, burns
output_coinvia linkedTreasuryCap, returns input fromreserve.
- Checks metadata binding, computes input via safe math, burns
rate(vault): u64reserve_value(vault): u64is_valid_owner_cap(vault, owner_cap): bool
calculate_output_amount_safe(rate: u64, input_value: u64, rate_decimals: u8): u64calculate_input_amount_safe(rate: u64, output_value: u64, rate_decimals: u8): u64
Both functions guard against division by zero and u64 overflow.
EWrongOwnerCap = 1EInsufficientReserves = 2EInvalidRate = 3EInvalidMetadata = 4EArithmeticOverflow = 5EDivisionByZero = 6
Unit tests under tests/vault_tests.move cover:
- Creation, deposit, withdraw, mint, redeem
- Failure paths: invalid rate, wrong owner cap, insufficient reserves, arithmetic overflow, division by zero
- Calculation correctness for varying precisions and edge cases
Run tests:
sui move testCreate a vault (example; replace types/ids as needed):
sui client call \
--package <PACKAGE_ID> \
--module vault \
--function create_vault \
--type-args <INPUT_COIN_TYPE> <OUTPUT_COIN_TYPE> \
--args <RATE:u64> <OUTPUT_TREASURY_CAP:ObjectID> <RATE_DECIMALS:u8> \
"$(printf 'VAULT' | xxd -p -c 256)" \
"$(printf 'My Vault' | xxd -p -c 256)" \
"$(printf 'Fixed-rate tokenized vault' | xxd -p -c 256)" \
noneMint:
sui client call \
--package <PACKAGE_ID> \
--module vault \
--function mint \
--type-args <INPUT_COIN_TYPE> <OUTPUT_COIN_TYPE> \
--args <VAULT:ObjectID> <VAULT_METADATA:ObjectID> <INPUT_COIN:ObjectID>Redeem:
sui client call \
--package <PACKAGE_ID> \
--module vault \
--function redeem \
--type-args <INPUT_COIN_TYPE> <OUTPUT_COIN_TYPE> \
--args <VAULT:ObjectID> <VAULT_METADATA:ObjectID> <OUTPUT_COIN:ObjectID>Owner operations (deposit, withdraw, set_rate) require OwnerCap that matches the vault id.