Shomari 1 месяц назад
Родитель
Сommit
fb897203e1

+ 3 - 3
web/components/Wallet/Setup.vue

@@ -21,7 +21,7 @@ const createWallet = () => {
 const importWallet = () => {
     // NOTE: This confirmation is NOT REQUIRED for single-application
     //       wallet integration(s), and can be SAFELY removed.
-    if (confirm('Before you continue, please close ALL other Studio browser windows. Failure to do so may result in LOSS OF ASSETS!\n\nWould you like to continue importing an existing wallet?')) {
+    if (confirm('Before you continue, please close ALL other Hush Your Money browser windows. Failure to do so may result in LOSS OF ASSETS!\n\nWould you like to continue importing an existing wallet?')) {
         /* Set/save mnemonic. */
         // NOTE: Will save `entropy` to the local storage.
         Wallet.setMnemonic(mnemonic.value)
@@ -42,7 +42,7 @@ const importWallet = () => {
 <template>
     <section class="flex flex-col gap-5">
         <p class="px-3 py-2 bg-yellow-100 text-base font-medium border-2 border-yellow-200 rounded-lg shadow-md">
-            Welcome to your Studio wallet.
+            Welcome to your Hush Your Money wallet.
             Click the button below to create a new wallet and begin trading.
         </p>
 
@@ -53,7 +53,7 @@ const importWallet = () => {
         <hr />
 
         <p class="px-3 py-2 bg-yellow-100 text-base font-medium border-2 border-yellow-200 rounded-lg shadow-md">
-            Import your existing wallet into Studio.
+            Import your existing wallet into Hush Your Money.
         </p>
 
         <textarea

+ 115 - 14
web/components/Admin/ChooseWallet.vue → web/components/Wallet/Welcome.vue

@@ -1,23 +1,120 @@
 <script setup lang="ts">
+/* Import modules. */
+import BCHJS from '@psf/bch-js'
+import { binToHex } from '@nexajs/utils'
+
 /* Define properties. */
 // https://vuejs.org/guide/components/props.html#props-declaration
 const props = defineProps({
     balances: Object,
     cashAddress: String,
+    utxos: Object,
+})
+
+/* Initialize stores. */
+import { useWalletStore } from '@/stores/wallet'
+const Wallet = useWalletStore()
+
+// REST API servers.
+const BCHN_MAINNET = 'https://bchn.fullstack.cash/v5/'
+
+const runtimeConfig = useRuntimeConfig()
+const jwtAuthToken = runtimeConfig.public.PSF_JWT_AUTH_TOKEN
+
+const balances = ref(null)
+const cashAddress = ref(null)
+
+// Instantiate bch-js based on the network.
+const bchjs = new BCHJS({
+    restURL: BCHN_MAINNET,
+    apiToken: jwtAuthToken,
 })
+// console.log('bchjs', bchjs)
 
-const clearWallet = () => {
 
+const calculateFee = () => {
+  // Calculate miner fees.
+  // Get byte count (x inputs, pay + remainder = 2x outputs)
+  return bchjs.BitcoinCash.getByteCount({ P2PKH: 1 }, { P2PKH: 2 })
 }
 
-const start = () => {
+// Combine all accounts inputs and outputs in one unsigned Tx
+const buildUnsignedTx = async () => {
+    try {
+        // console.log(`inputs: ${JSON.stringify(combinedInputs, null, 2)}`)
+
+        const fee = await calculateFee()
+        const paymentAmount = props.balances.confirmed - fee
+        const satsNeeded = fee + paymentAmount
+        const receiver = 'bitcoincash:qq27zfgmy7hckrrxygjdz6rr847pjkzzyc6d0un4e4'
+        console.log(`payment (+fee): ${satsNeeded}`)
+
+        const transactionBuilder = new bchjs.TransactionBuilder()
+
+        props.utxos.forEach(async function (_utxo) {
+            const utxo = _utxo
+            console.log('UTXO', utxo);
+            transactionBuilder.addInput(utxo.tx_hash, utxo.tx_pos)
+            const originalAmount = utxo.value
+            const remainder = originalAmount - satsNeeded
+            console.log('REMAINDER', remainder);
+
+
+            if (remainder < 0) {
+                throw new Error('Selected UTXO does not have enough satoshis')
+            }
+
+            // Send payment
+            transactionBuilder.addOutput(receiver, satsNeeded)
+
+            // Send the BCH change back to the payment part
+            // transactionBuilder.addOutput(account.address, remainder - 300)
+        })
 
+        const tx = transactionBuilder.transaction.buildIncomplete()
+        const hex = tx.toHex()
+        console.log(`Non-signed Tx hex: ${hex}`)
+        // fs.writeFileSync('unsigned_tx.json', JSON.stringify(hex, null, 2))
+        // console.log('unsigned_tx.json written successfully.')
+    } catch (err) {
+        console.error(`Error in buildUnsignedTx(): ${err}`)
+        throw err
+    }
 }
 
-// onMounted(() => {
-//     console.log('Mounted!')
-//     // Now it's safe to perform setup operations.
-// })
+
+const cashout = () => {
+    alert('WIP?? sorry...')
+}
+
+const start = async () => {
+    console.log('Starting...')
+
+    console.log('FEE', calculateFee())
+
+    buildUnsignedTx()
+
+    const readyToFuse = props.utxos
+    console.log('READY TO FUSE', readyToFuse)
+
+    // TODO Handle any filtering required BEFORE submitting for fusion.
+
+    const response = await $fetch('/v1', {
+        method: 'POST',
+        body: {
+            authid: binToHex(Wallet.wallet.publicKey),
+            coins: readyToFuse,
+            tokens: [],
+        },
+    })
+    .catch(err => console.error(err))
+    console.log('RESPONSE', response)
+}
+
+onMounted(() => {
+    // console.log('Mounted!', props.balances)
+    // Now it's safe to perform setup operations.
+})
 
 // onBeforeUnmount(() => {
 //     console.log('Before Unmount!')
@@ -46,19 +143,23 @@ const start = () => {
             </h3>
 
             <h3>
-                Confirmed: {{balances?.confirmed}}
+                Confirmed: {{props.balances?.confirmed}}
             </h3>
             <h3>
-                Unconfirmed: {{balances?.unconfirmed}}
+                Unconfirmed: {{props.balances?.unconfirmed}}
             </h3>
 
-            <button @click="clearWallet">
-                Clear wallet
-            </button>
+            <div class="my-3 grid grid-cols-2 gap-4">
+                <button @click="cashout" class="px-3 py-2 bg-lime-200 border-2 border-lime-400 text-2xl text-lime-800 font-medium rounded shadow hover:bg-lime-100">
+                    Cashout Wallet
+                </button>
+
+                <button @click="start" class="px-3 py-2 bg-lime-200 border-2 border-lime-400 text-2xl text-lime-800 font-medium rounded shadow hover:bg-lime-100">
+                    Start Fusions
+                </button>
+            </div>
 
-            <button @click="start">
-                Start
-            </button>
+            <pre class="text-xs">{{props.utxos}}</pre>
 
         </section>
 

+ 5 - 19
web/handlers/initFusions.ts

@@ -1,7 +1,10 @@
 /* Initialize globals. */
 let fusionsDb
 
-const setupGlobalDb = async (_fusionsDb) => {
+/**
+ * Initialize
+ */
+const init = async (_fusionsDb) => {
     fusionsDb = _fusionsDb
 
     fusionsDb['4e9654f9-3de9-4f9a-8169-3834f40847f5'] = {
@@ -36,28 +39,11 @@ const setupGlobalDb = async (_fusionsDb) => {
     }
 }
 
-const init = async () => {
-    setTimeout(() => {
-        console.log('ADD ONE MORE', fusionsDb)
-        fusionsDb['af371828-7199-4e4e-baca-bcd11d01edda'] = {
-            tierid: 5600000,
-            guests: 2,
-            inputs: 0,
-            outputs: 0,
-            createdAt: 1723245503,
-            updatedAt: 1723245503,
-        }
-
-    }, 10000)
-}
 
 /**
  * Initialize Fusions
  */
 export default async (_fusionsDb) => {
-    /* Setup global database. */
-    setupGlobalDb(_fusionsDb)
-
     /* Initialize. */
-    init()
+    init(_fusionsDb)
 }

+ 40 - 0
web/pages/admin/index.vue

@@ -21,6 +21,9 @@ const profiles = ref(null)
 const status = ref(null)
 const system = ref(null)
 
+const clubAddress = ref(null)
+const clubPubkey = ref(null)
+
 const displayCreatedAt = computed(() => {
     if (!status.value || !status.value.createdAt) {
         return 'loading...'
@@ -98,6 +101,14 @@ const init = async () => {
     profiles.value = await $fetch('/api/profiles')
         .catch(err => console.error(err))
     // console.log('PROFILES', profiles.value)
+
+    /* Request wallet. */
+    const response = await $fetch('/api/wallet')
+        .catch(err => console.error(err))
+    console.log('WALLET', response)
+
+    clubAddress.value = response.address
+    clubPubkey.value = response.publicKey
 }
 
 onMounted(() => {
@@ -176,6 +187,35 @@ onMounted(() => {
                     {{displayTimeAgo}}
                 </h3>
             </section>
+
+            <section class="w-full px-5 py-3 flex flex-col gap-0 bg-amber-100 border-2 border-amber-300 rounded-2xl shadow">
+                <h2 class="text-amber-500 text-base font-bold tracking-widest uppercase">
+                    Club Wallet
+                </h2>
+
+                <NuxtLink :to="'https://explorer.nexa.org/address/' + clubAddress" target="_blank" class="text-amber-700 text-base font-medium tracking-tight hover:text-amber-600 hover:underline">
+                    {{clubAddress}}
+                </NuxtLink>
+
+                <h3 class="text-amber-700 text-base text-right font-medium italic">
+                    Current signer of ALL Club messages
+                </h3>
+            </section>
+
+            <section class="w-full px-5 py-3 flex flex-col gap-0 bg-amber-100 border-2 border-amber-300 rounded-2xl shadow">
+                <h2 class="text-amber-500 text-base font-bold tracking-widest uppercase">
+                    Club Public Key
+                </h2>
+
+                <h3 class="text-amber-700 text-xs font-medium tracking-tight">
+                    {{clubPubkey}}
+                </h3>
+
+                <h3 class="text-amber-700 text-base text-right font-medium italic">
+                    Use this key to sign encrypted messages
+                </h3>
+            </section>
+
         </div>
 
         <pre class="text-xs">{{system}}</pre>

+ 10 - 10
web/pages/admin/liquidity.vue

@@ -7,13 +7,12 @@ const BCHN_MAINNET = 'https://bchn.fullstack.cash/v5/'
 
 const runtimeConfig = useRuntimeConfig()
 const jwtAuthToken = runtimeConfig.public.PSF_JWT_AUTH_TOKEN
-// console.log('jwtAuthToken', jwtAuthToken)
 
 const balances = ref(null)
 const cashAddress = ref(null)
+const utxos = ref(null)
 
 // Instantiate bch-js based on the network.
-// FIXME https://github.com/Permissionless-Software-Foundation/jwt-bch-demo/blob/master/lib/fullstack-jwt.js
 const bchjs = new BCHJS({
     restURL: BCHN_MAINNET,
     apiToken: jwtAuthToken,
@@ -45,6 +44,8 @@ const init = async () => {
     let response
     let rootSeed
 
+    utxos.value = []
+
     /* Set root seed. */
     rootSeed = await bchjs.Mnemonic.toSeed(Wallet.mnemonic)
     // console.log('rootSeed', rootSeed)
@@ -62,11 +63,11 @@ const init = async () => {
     // console.log('cashAddress', cashAddress.value)
 
     response = await bchjs.Electrumx.utxo(cashAddress.value)
-    console.log('RESPONSE', response)
+    // console.log('RESPONSE', response)
 
     /* Set UTXOs. */
-    const utxos = response.utxos
-    console.log('UTXOS', JSON.stringify(utxos, null, 2))
+    utxos.value = response.utxos
+    console.log('UTXOS', JSON.stringify(utxos.value, null, 2))
 
     /* Request balances. */
     response = await bchjs.Electrumx.balance(cashAddress.value)
@@ -100,17 +101,16 @@ onMounted(() => {
             Lorem, ipsum dolor sit amet consectetur adipisicing elit. Id eius voluptatem minus natus at eveniet dolorum eos mollitia, maxime animi excepturi harum omnis illum odit recusandae pariatur! Unde, explicabo molestias.
         </p>
 
-        <section class="py-10 flex justify-center">
+        <section class="w-full w-3/4 py-10 flex justify-center">
             <Loading v-if="Wallet.isLoading" />
 
-            <WalletSetup v-else-if="!Wallet.isReady" class="w-3/4" />
+            <WalletSetup v-else-if="!Wallet.isReady" />
 
-            <AdminChooseWallet v-else
-                class="w-3/4"
+            <WalletWelcome v-else
                 :balances="balances"
                 :cashAddress="cashAddress"
+                :utxos="utxos"
             />
-
         </section>
     </main>
 </template>

+ 13 - 29
web/server/routes/_wallet.get.ts → web/server/api/wallet.get.ts

@@ -1,6 +1,6 @@
 /* Import modules. */
-import { Wallet } from '@nexajs/wallet'
 import BCHJS from '@psf/bch-js'
+import { binToHex } from '@nexajs/utils'
 
 // REST API servers.
 const BCHN_MAINNET = 'https://bchn.fullstack.cash/v5/'
@@ -17,9 +17,6 @@ const bchjs = new BCHJS({
 })
 // console.log('bchjs', bchjs)
 
-/* Initialize Wallet (instance) */
-let wallet
-
 const lang = 'english' // Set the language of the wallet.
 
 const aliceObj = {}
@@ -101,39 +98,26 @@ async function createWallets () {
   }
 }
 
-/**
- * Initialize (Wallet)
- *
- * Setup an "ephemeral" wallet for the use of this Club Session.
- */
-const init = async () => {
-    wallet = await Wallet.init()
-        .catch(err => console.error(err))
-    // console.log('WALLET', wallet)
-}
-
-init()
-// createWallets()
-
 export default defineEventHandler((event) => {
-    /* Set project mnemonic. */
-    // const mnemonic = process.env.PROJECT_MNEMONIC
+    /* Set database. */
+    const Db = event.context.Db
+    // console.log('DB', Db)
 
-    /* Build wallet. */
-    // const wallet = {
-    //     mnemonic,
-    // }
+    /* Set database. */
+    const Wallet = event.context.Wallet
+    // console.log('WALLET', Wallet)
 
     const walletPkg = {
-        address: wallet.address,
-        assets: JSON.stringify(wallet.assets, (key, value) =>
+        address: Wallet.address,
+        publicKey: binToHex(Wallet.publicKey),
+        assets: JSON.stringify(Wallet.assets, (key, value) =>
             typeof value === 'bigint' ? value.toString() + 'n' : value
         ),
-        coins: JSON.stringify(wallet.coins, (key, value) =>
+        coins: JSON.stringify(Wallet.coins, (key, value) =>
             typeof value === 'bigint' ? value.toString() + 'n' : value
         ),
-        mnemonic: wallet.mnemonic,
-        tokens: JSON.stringify(wallet.tokens, (key, value) =>
+        mnemonic: Wallet.mnemonic,
+        tokens: JSON.stringify(Wallet.tokens, (key, value) =>
             typeof value === 'bigint' ? value.toString() + 'n' : value
         ),
         aliceObj,

+ 0 - 0
web/server/middleware/cors.ts → web/server/middleware/01-cors.ts


+ 3 - 7
web/server/middleware/db.ts → web/server/middleware/02-db.ts

@@ -6,13 +6,11 @@ import initFusions from '../../handlers/initFusions.ts'
 console.info('Initializing Ephemeral Database Manager...')
 
 /* Initialize locals. */
-let response
 let status
 
 /* Initialize DB handlers. */
 let fusionsDb
 let profilesDb
-// let sessionsDb
 let systemDb
 
 /**
@@ -82,9 +80,8 @@ const init = async () => {
             updatedAt: moment().unix(),
         },
 
-        response = await put('system', 'status', status)
+        put('system', 'status', status)
             .catch(err => console.error(err))
-        // console.log('RESPONSE (system)', response)
     } else {
         /* Set status. */
         status = systemDb.status
@@ -93,9 +90,8 @@ const init = async () => {
         status.updatedAt = moment().unix()
 
         /* Save data to store. */
-        response = await put('system', 'status', status)
+        put('system', 'status', status)
             .catch(err => console.error(err))
-        // console.log('RESPONSE (status)', response)
     }
 
     // console.log('SYSTEM (DB) STATUS', systemDb.status)
@@ -107,11 +103,11 @@ initFusions(fusionsDb)
 export default defineEventHandler((event) => {
     // console.log('Ephemeral Db Request: ' + getRequestURL(event))
 
+    /* Inject database into server context. */
     event.context.Db = {
         /* Getters */
         fusions: fusionsDb,
         profiles: profilesDb,
-        // sessions: sessionsDb,
         system: systemDb,
 
         /* Setters */

+ 26 - 0
web/server/middleware/03-wallet.ts

@@ -0,0 +1,26 @@
+/* Import modules. */
+import { Wallet } from '@nexajs/wallet'
+
+/* Initialize Wallet (instance) */
+let wallet
+
+/**
+ * Initialize (Wallet)
+ *
+ * Setup an "ephemeral" wallet for the use of this Club Session.
+ */
+const init = async () => {
+    wallet = await Wallet.init()
+        .catch(err => console.error(err))
+    // console.log('WALLET INIT', wallet)
+}
+init()
+
+export default defineEventHandler((event) => {
+    /* Set database. */
+    const Db = event.context.Db
+    // console.log('DB', Db)
+
+    /* Inject wallet into server context. */
+    event.context.Wallet = wallet
+})

+ 9 - 6
web/server/routes/v1.post.ts

@@ -13,7 +13,7 @@ export default defineEventHandler(async (event) => {
 
     /* Set (request) body. */
     const body = await readBody(event)
-    // console.log('BODY', body)
+    console.log('BODY', body)
 
     if (!body) {
         return `Authorization FAILED!`
@@ -22,15 +22,13 @@ export default defineEventHandler(async (event) => {
     /* Set profile parameters. */
     const authid = body.authid
     console.log('AUTH ID', authid)
+
     const actionid = body.actionid
     console.log('ACTION ID', actionid)
+
     const sessionid = body.sessionid
     console.log('SESSION ID', sessionid)
 
-    console.log({
-        authid,
-    })
-
     /* Initialize locals. */
     let params
     let profile
@@ -38,7 +36,12 @@ export default defineEventHandler(async (event) => {
     let session
     let success
 
-    // FIXME Validate authid
+    /* Validate auth ID. */
+    if (typeof authid === 'undefined' || authid === null) {
+        setResponseStatus(event, 401)
+
+        return 'Authorization failed!'
+    }
 
     /* Request session. */
     profile = Db.profiles[authid]