solana_libra_stdlib 0.0.0

Solana Libra stdlib
// The module for the account resource that governs every Libra account
module LibraAccount {
    import 0x0.LibraCoin;
    import 0x00.Hash;

    // Every Libra account has a LibraAccount.T resource
    resource T {
        // The coins stored in this account
        balance: R#LibraCoin.T,
        // The current authentication key.
        // This can be different than the key used to create the account
        authentication_key: bytearray,
        // The current sequence number.
        // Incremented by one each time a transaction is submitted
        sequence_number: u64,
        // TEMPORARY the current count for the number of sent events for this account
        // The events system is being overhauled and this will be replaced
        sent_events_count: u64,
        // TEMPORARY the current count for the number of received events for this account
        // The events system is being overhauled and this will be replaced
        received_events_count: u64
    }

    // Message for sent events
    struct SentPaymentEvent {
        // The address that was paid
        payee: address,
        // The amount of LibraCoin.T sent
        amount: u64,
    }

    // Message for received events
    struct ReceivedPaymentEvent {
        // The address that sent the coin
        payer: address,
        // The amount of LibraCoin.T received
        amount: u64,
    }

    // Creates a new LibraAccount.T
    // Invoked by the `create_account` builtin
    make(auth_key: bytearray): R#Self.T {
        let zero_balance: R#LibraCoin.T;
        zero_balance = LibraCoin.zero();
        return T {
            balance: move(zero_balance),
            authentication_key: move(auth_key),
            sequence_number: 0,
            sent_events_count: 0,
            received_events_count: 0,
        };
    }

    // Deposits the `to_deposit` coin into the `payee`'s account
    public deposit(payee: address, to_deposit: R#LibraCoin.T) {
        let deposit_value: u64;
        let payee_account_ref: &mut R#Self.T;
        let sender: address;
        let sender_account_ref: &mut R#Self.T;
        let sent_event: V#Self.SentPaymentEvent;
        let received_event: V#Self.ReceivedPaymentEvent;

        // Check that the `to_deposit` coin is non-zero
        deposit_value = LibraCoin.value(&to_deposit);
        assert(copy(deposit_value) > 0, 7);

        // Load the sender's account
        sender = get_txn_sender();
        sender_account_ref = borrow_global<T>(copy(sender));
        // Log a send event
        sent_event = SentPaymentEvent { payee: copy(payee), amount: copy(deposit_value) };
        // TEMPORARY The events system is being overhauled and this will be replaced by something
        // more principled in the future
        emit_event(&mut move(sender_account_ref).sent_events_count, b"73656E745F6576656E74735F636F756E74", move(sent_event));

        // Load the payee's account
        payee_account_ref = borrow_global<T>(move(payee));
        // Deposit the `to_deposit` coin
        LibraCoin.deposit(&mut copy(payee_account_ref).balance, move(to_deposit));
        // Log a received event
        received_event = ReceivedPaymentEvent { payer: move(sender), amount: move(deposit_value) };
        // TEMPORARY The events system is being overhauled and this will be replaced by something
        // more principled in the future
        emit_event(&mut move(payee_account_ref).received_events_count, b"72656365697665645F6576656E74735F636F756E74", move(received_event));
        return;
    }

    // mint_to_address can only be called by accounts with MintCapability (see LibraCoin)
    // and those account will be charged for gas. If those account don't have enough gas to pay
    // for the transaction cost they will fail minting.
    // However those account can also mint to themselves so that is a decent workaround
    public mint_to_address(payee: address, amount: u64) {
        // Create an account if it does not exist
        if (!exists<T>(copy(payee))) {
            Self.create_new_account(copy(payee), 0);
        }

        // Mint and deposit the coin
        Self.deposit(move(payee), LibraCoin.mint_with_default_capability(move(amount)));
        return;
    }

    // Helper to withdraw `amount` from the given `account` and return the resulting LibraCoin.T
    withdraw_from_account(account: &mut R#Self.T, amount: u64): R#LibraCoin.T {
        let to_withdraw: R#LibraCoin.T;
        to_withdraw = LibraCoin.withdraw(&mut move(account).balance, copy(amount));
        return move(to_withdraw);
    }

    // Withdraw `amount` LibraCoin.T from the transaction sender's account
    public withdraw_from_sender(amount: u64): R#LibraCoin.T {
        let sender: address;
        let sender_account: &mut R#Self.T;
        let to_withdraw: R#LibraCoin.T;

        // Load the sender
        sender = get_txn_sender();
        sender_account = borrow_global<T>(move(sender));

        // Withdraw the coin
        to_withdraw = Self.withdraw_from_account(move(sender_account), move(amount));
        return move(to_withdraw);
    }

    // Withdraw `amount` LibraCoin.T from the transaction sender's account and send the coin
    // to the `payee` address
    // Creates the `payee` account if it does not exist
    public pay_from_sender(payee: address, amount: u64) {
        let to_pay: R#LibraCoin.T;
        let payee_exists: bool;
        payee_exists = exists<T>(copy(payee));
        if (move(payee_exists)) {
            to_pay = Self.withdraw_from_sender(move(amount));
            Self.deposit(move(payee), move(to_pay));
        } else {
            Self.create_new_account(move(payee), move(amount));
        }
        return;
    }

    // Rotate the transaction sender's authentication key
    // The new key will be used for signing future transactions
    public rotate_authentication_key(new_authentication_key: bytearray) {
        let sender: address;
        let sender_account: &mut R#Self.T;
        sender = get_txn_sender();
        sender_account = borrow_global<T>(move(sender));
        *(&mut move(sender_account).authentication_key) = move(new_authentication_key);
        return;
    }

    // Creates a new account at `fresh_address` with the `initial_balance` deducted from the
    // transaction sender's account
    public create_new_account(fresh_address: address, initial_balance: u64) {
        create_account(copy(fresh_address));
        if (copy(initial_balance) > 0) {
            Self.pay_from_sender(move(fresh_address), move(initial_balance));
        }
        return;
    }

    // Helper to return u64 value of the `balance` field for given `account`
    balance_for_account(account: &R#Self.T): u64 {
        let balance_value: u64;
        balance_value = LibraCoin.value(&move(account).balance);
        return move(balance_value);
    }

    // Return the current balance of the LibraCoin.T in LibraAccount.T at `addr`
    public balance(addr: address): u64 {
        let payee_account: &mut R#Self.T;
        let imm_payee_account: &R#Self.T;
        let balance_amount: u64;
        payee_account = borrow_global<T>(move(addr));
        imm_payee_account = freeze(move(payee_account));
        balance_amount = Self.balance_for_account(move(imm_payee_account));
        return move(balance_amount);
    }

    // Helper to return the sequence number field for given `account`
    sequence_number_for_account(account: &R#Self.T): u64 {
        return *(&move(account).sequence_number);
    }

    // Return the current sequence number at `addr`
    public sequence_number(addr: address): u64 {
        let account_ref: &mut R#Self.T;
        let imm_ref: &R#Self.T;
        let sequence_number_value: u64;
        account_ref = borrow_global<T>(move(addr));
        imm_ref = freeze(move(account_ref));
        sequence_number_value = Self.sequence_number_for_account(move(imm_ref));
        return move(sequence_number_value);
    }

    // Checks if an account exists at `check_addr`
    public exists(check_addr: address): bool {
        let is_present: bool;
        is_present = exists<T>(move(check_addr));
        return move(is_present);
    }

    // The prologue is invoked at the beginning of every transaction
    // It verifies:
    // - The account's auth key matches the transaction's public key
    // - That the account has enough balance to pay for all of the gas
    // - That the sequence number matches the transaction's sequence key
    prologue() {
        let transaction_sender: address;
        let transaction_sender_exists: bool;
        let sender_account: &mut R#Self.T;
        let imm_sender_account: &R#Self.T;
        let sender_public_key: bytearray;
        let public_key_hash: bytearray;
        let gas_price: u64;
        let gas_units: u64;
        let gas_fee: u64;
        let balance_amount: u64;
        let sequence_number_value: u64;
        let transaction_sequence_number_value: u64;

        transaction_sender = get_txn_sender();

        // FUTURE: Make these error codes sequential
        // Verify that the transaction sender's account exists
        transaction_sender_exists = exists<T>(copy(transaction_sender));
        assert(move(transaction_sender_exists), 5);

        // Load the transaction sender's account
        sender_account = borrow_global<T>(copy(transaction_sender));

        // Check that the transaction's public key matches the account's current auth key
        sender_public_key = get_txn_public_key();
        public_key_hash = Hash.sha3_256(move(sender_public_key));
        assert(move(public_key_hash) == *(&copy(sender_account).authentication_key), 2);

        // Check that the account has enough balance for all of the gas
        gas_price = get_txn_gas_unit_price();
        gas_units = get_txn_max_gas_units();
        gas_fee = move(gas_price) * move(gas_units);
        imm_sender_account = freeze(copy(sender_account));
        balance_amount = Self.balance_for_account(move(imm_sender_account));
        assert(move(balance_amount) >= move(gas_fee), 6);

        // Check that the transaction sequence number matches the sequence number of the account
        sequence_number_value = *(&mut move(sender_account).sequence_number);
        transaction_sequence_number_value = get_txn_sequence_number();
        assert(copy(transaction_sequence_number_value) >= copy(sequence_number_value), 3);
        assert(move(transaction_sequence_number_value) == move(sequence_number_value), 4);
        return;
    }

    // The epilogue is invoked at the end of transactions.
    // It collects gas and bumps the sequence number
    epilogue() {
        let transaction_sender: address;
        let sender_account: &mut R#Self.T;
        let imm_sender_account: &R#Self.T;
        let gas_price: u64;
        let gas_units_remaining: u64;
        let starting_gas_units: u64;
        let gas_fee_amount: u64;
        let balance_amount: u64;
        let gas_fee: R#LibraCoin.T;
        let transaction_sequence_number_value: u64;

        transaction_sender = get_txn_sender();

        // Load the transaction sender's account
        sender_account = borrow_global<T>(copy(transaction_sender));

        // Charge for gas
        gas_price = get_txn_gas_unit_price();
        starting_gas_units = get_txn_max_gas_units();
        gas_units_remaining = get_gas_remaining();
        gas_fee_amount = move(gas_price) * (move(starting_gas_units) - move(gas_units_remaining));
        imm_sender_account = freeze(copy(sender_account));
        balance_amount = Self.balance_for_account(move(imm_sender_account));
        assert(move(balance_amount) >= copy(gas_fee_amount), 6);

        gas_fee = Self.withdraw_from_account(copy(sender_account), move(gas_fee_amount));
        LibraCoin.TODO_REMOVE_burn_gas_fee(move(gas_fee));

        // Bump the sequence number
        transaction_sequence_number_value = get_txn_sequence_number();
        *(&mut move(sender_account).sequence_number) = move(transaction_sequence_number_value) + 1;
        return;
    }

}