address 0x2 { module Map { native struct T has copy, drop, store; native public fun empty(): T; native public fun get(m: &T, k: &K): &V; native public fun get_mut(m: &mut T, k: &K): &mut V; native public fun contains_key(m: &T, k: &K): bool; // throws on duplicate as I don't feel like mocking up Option native public fun insert(m: &T, k: K, v: V); // throws on miss as I don't feel like mocking up Option native public fun remove(m: &T, k: &K): V; } } address 0x2 { module Token { struct Coin has store { type: AssetType, value: u64, } // control the minting/creation in the defining module of `ATy` public fun create(type: ATy, value: u64): Coin { Coin { type, value } } public fun value(coin: &Coin): u64 { coin.value } public fun split(coin: Coin, amount: u64): (Coin, Coin) { let other = withdraw(&mut coin, amount); (coin, other) } public fun withdraw(coin: &mut Coin, amount: u64): Coin { assert!(coin.value >= amount, 10); coin.value = coin.value - amount; Coin { type: *&coin.type, value: amount } } public fun join(xus: Coin, coin2: Coin): Coin { deposit(&mut xus, coin2); xus } public fun deposit(coin: &mut Coin, check: Coin) { let Coin { value, type } = check; assert!(&coin.type == &type, 42); coin.value = coin.value + value; } public fun destroy_zero(coin: Coin) { let Coin { value, type: _ } = coin; assert!(value == 0, 11) } } } address 0x3 { module OneToOneMarket { use std::signer; use 0x2::Map; use 0x2::Token; struct Pool has key { coin: Token::Coin, } struct DepositRecord has key { // pool owner => amount record: Map::T } struct BorrowRecord has key { // pool owner => amount record: Map::T } struct Price has key { price: u64, } fun accept(account: &signer, init: Token::Coin) { let sender = signer::address_of(account); assert!(!exists>(sender), 42); move_to(account, Pool { coin: init }) } public fun register_price( account: &signer, initial_in: Token::Coin, initial_out: Token::Coin, price: u64 ) { accept(account, initial_in); accept(account, initial_out); move_to(account, Price { price }) } public fun deposit(account: &signer, pool_owner: address, coin: Token::Coin) acquires Pool, DepositRecord { let amount = Token::value(&coin); update_deposit_record(account, pool_owner, amount); let pool = borrow_global_mut>(pool_owner); Token::deposit(&mut pool.coin, coin) } public fun borrow( account: &signer, pool_owner: address, amount: u64, ): Token::Coin acquires Price, Pool, DepositRecord, BorrowRecord { assert!(amount <= max_borrow_amount(account, pool_owner), 1025); update_borrow_record(account, pool_owner, amount); let pool = borrow_global_mut>(pool_owner); Token::withdraw(&mut pool.coin, amount) } fun max_borrow_amount(account: &signer, pool_owner: address): u64 acquires Price, Pool, DepositRecord, BorrowRecord { let input_deposited = deposited_amount(account, pool_owner); let output_deposited = borrowed_amount(account, pool_owner); let input_into_output = input_deposited * borrow_global>(pool_owner).price; let max_output = if (input_into_output < output_deposited) 0 else (input_into_output - output_deposited); let available_output = { let pool = borrow_global>(pool_owner); Token::value(&pool.coin) }; if (max_output < available_output) max_output else available_output } fun update_deposit_record(account: &signer, pool_owner: address, amount: u64) acquires DepositRecord { let sender = signer::address_of(account); if (!exists>(sender)) { move_to(account, DepositRecord { record: Map::empty() }) }; let record = &mut borrow_global_mut>(sender).record; if (Map::contains_key(record, &pool_owner)) { let old_amount = Map::remove(record, &pool_owner); amount = amount + old_amount; }; Map::insert(record, pool_owner, amount) } fun update_borrow_record(account: &signer, pool_owner: address, amount: u64) acquires BorrowRecord { let sender = signer::address_of(account); if (!exists>(sender)) { move_to(account, BorrowRecord { record: Map::empty() }) }; let record = &mut borrow_global_mut>(sender).record; if (Map::contains_key(record, &pool_owner)) { let old_amount = Map::remove(record, &pool_owner); amount = amount + old_amount; }; Map::insert(record, pool_owner, amount) } fun deposited_amount(account: &signer, pool_owner: address): u64 acquires DepositRecord { let sender = signer::address_of(account); if (!exists>(sender)) return 0; let record = &borrow_global>(sender).record; if (Map::contains_key(record, &pool_owner)) *Map::get(record, &pool_owner) else 0 } fun borrowed_amount(account: &signer, pool_owner: address): u64 acquires BorrowRecord { let sender = signer::address_of(account); if (!exists>(sender)) return 0; let record = &borrow_global>(sender).record; if (Map::contains_key(record, &pool_owner)) *Map::get(record, &pool_owner) else 0 } } } address 0x70DD { module ToddNickels { use 0x2::Token; use std::signer; struct T has copy, drop, store {} struct Wallet has key { nickels: Token::Coin, } public fun init(account: &signer) { assert!(signer::address_of(account) == @0x70DD, 42); move_to(account, Wallet { nickels: Token::create(T{}, 0) }) } public fun mint(account: &signer): Token::Coin { assert!(signer::address_of(account) == @0x70DD, 42); Token::create(T{}, 5) } public fun destroy(c: Token::Coin) acquires Wallet { Token::deposit(&mut borrow_global_mut(@0x70DD).nickels, c) } } }