|  |  | @@ -26,59 +26,43 @@ | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | </section> | 
		
	
		
			
			|  |  |  | <section> | 
		
	
		
			
			|  |  |  | <section class="section is-small"> | 
		
	
		
			
			|  |  |  | <div class="box"> | 
		
	
		
			
			|  |  |  | <div class="title is-size-4 has-text-grey-dark has-text-centered"> | 
		
	
		
			
			|  |  |  | Tracker | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | <div class="level" v-if="fund.fundInfo.bonuses.length > 0"> | 
		
	
		
			
			|  |  |  | <div class="level-item has-text-centered" | 
		
	
		
			
			|  |  |  | v-for="bonus in fund.fundInfo.bonuses.sort((v1, v2) => v1.goal - v2.goal)" | 
		
	
		
			
			|  |  |  | v-bind:key="bonus.goal" | 
		
	
		
			
			|  |  |  | > | 
		
	
		
			
			|  |  |  | <div> | 
		
	
		
			
			|  |  |  | <p | 
		
	
		
			
			|  |  |  | class="heading" | 
		
	
		
			
			|  |  |  | :class="amountHeld >= bonus.goal | 
		
	
		
			
			|  |  |  | <div class="mb-4"> | 
		
	
		
			
			|  |  |  | <div class="level" v-if="fund.fundInfo.bonuses.length > 0"> | 
		
	
		
			
			|  |  |  | <div class="level-item has-text-centered" | 
		
	
		
			
			|  |  |  | v-for="bonus in fund.fundInfo.bonuses.sort((v1, v2) => v1.goal - v2.goal)" | 
		
	
		
			
			|  |  |  | v-bind:key="bonus.goal" | 
		
	
		
			
			|  |  |  | > | 
		
	
		
			
			|  |  |  | <div> | 
		
	
		
			
			|  |  |  | <p | 
		
	
		
			
			|  |  |  | class="heading" | 
		
	
		
			
			|  |  |  | :class="amountHeld >= bonus.goal | 
		
	
		
			
			|  |  |  | ? 'has-text-success' : 'has-text-grey-dark'" | 
		
	
		
			
			|  |  |  | > | 
		
	
		
			
			|  |  |  | {{ bonus.goal.toLocaleString() }} XLM | 
		
	
		
			
			|  |  |  | </p> | 
		
	
		
			
			|  |  |  | <p | 
		
	
		
			
			|  |  |  | class="title" | 
		
	
		
			
			|  |  |  | :class="amountHeld >= bonus.goal | 
		
	
		
			
			|  |  |  | > | 
		
	
		
			
			|  |  |  | {{ bonus.goal.toLocaleString() }} XLM | 
		
	
		
			
			|  |  |  | </p> | 
		
	
		
			
			|  |  |  | <p | 
		
	
		
			
			|  |  |  | class="title" | 
		
	
		
			
			|  |  |  | :class="amountHeld >= bonus.goal | 
		
	
		
			
			|  |  |  | ? 'has-text-success' : 'has-text-grey-dark'" | 
		
	
		
			
			|  |  |  | > | 
		
	
		
			
			|  |  |  | {{ bonus.percent }}% | 
		
	
		
			
			|  |  |  | </p> | 
		
	
		
			
			|  |  |  | > | 
		
	
		
			
			|  |  |  | {{ bonus.percent }}% | 
		
	
		
			
			|  |  |  | </p> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | <div v-else> | 
		
	
		
			
			|  |  |  | <div class="has-text-centered">This group fund has no rewards available</div> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | <div | 
		
	
		
			
			|  |  |  | class="is-flex | 
		
	
		
			
			|  |  |  | is-flex-direction-row | 
		
	
		
			
			|  |  |  | is-justify-content-space-around | 
		
	
		
			
			|  |  |  | is-size-4 | 
		
	
		
			
			|  |  |  | has-text-white | 
		
	
		
			
			|  |  |  | mb-3" | 
		
	
		
			
			|  |  |  | > | 
		
	
		
			
			|  |  |  | <div class="total"> | 
		
	
		
			
			|  |  |  | <span class="total-label is-size-3 pr-2 has-text-weight-light"> | 
		
	
		
			
			|  |  |  | Raised | 
		
	
		
			
			|  |  |  | </span> | 
		
	
		
			
			|  |  |  | <span class="pl-3 has-text-weight-bold"> | 
		
	
		
			
			|  |  |  | {{ amountHeld.toLocaleString() }} XLM | 
		
	
		
			
			|  |  |  | </span> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | <div class="remaining"> | 
		
	
		
			
			|  |  |  | <span class="remaining-label is-size-3 pr-2 has-text-weight-light"> | 
		
	
		
			
			|  |  |  | Remaining | 
		
	
		
			
			|  |  |  | </span> | 
		
	
		
			
			|  |  |  | <span class="pl-3 has-text-weight-bold"> | 
		
	
		
			
			|  |  |  | {{ amountAvailable.toLocaleString() }} XLM | 
		
	
		
			
			|  |  |  | </span> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | <progress class="progress is-large is-info" :value="calcPctComplete()" | 
		
	
		
			
			|  |  |  | max="100"> | 
		
	
		
			
			|  |  |  | {{ calcPctComplete() }}% | 
		
	
		
			
			|  |  |  | </progress> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | </section> | 
		
	
		
			
			|  |  |  | <section class="section is-small"> | 
		
	
	
		
			
				|  |  | @@ -96,7 +80,7 @@ | 
		
	
		
			
			|  |  |  | type="number" | 
		
	
		
			
			|  |  |  | placeholder="Amount" | 
		
	
		
			
			|  |  |  | aria-label="Amount" | 
		
	
		
			
			|  |  |  | v-model="amt" | 
		
	
		
			
			|  |  |  | v-model="amount" | 
		
	
		
			
			|  |  |  | :max="acctBalance" | 
		
	
		
			
			|  |  |  | :disabled="loading.balance" | 
		
	
		
			
			|  |  |  | > | 
		
	
	
		
			
				|  |  | @@ -145,26 +129,56 @@ | 
		
	
		
			
			|  |  |  | <th>Wallet</th> | 
		
	
		
			
			|  |  |  | <th>Amount</th> | 
		
	
		
			
			|  |  |  | <th v-if="!enableConsolidation">Time</th> | 
		
	
		
			
			|  |  |  | <th v-else>Tokens</th> | 
		
	
		
			
			|  |  |  | <template v-else> | 
		
	
		
			
			|  |  |  | <th>Bonus</th> | 
		
	
		
			
			|  |  |  | <th v-if="store.getters.getToken && hasPermission(Privileges.Admin)">Post</th> | 
		
	
		
			
			|  |  |  | </template> | 
		
	
		
			
			|  |  |  | </tr> | 
		
	
		
			
			|  |  |  | </thead> | 
		
	
		
			
			|  |  |  | <tbody> | 
		
	
		
			
			|  |  |  | <tr v-for="(contribution, i) in contributions" v-bind:key="i"> | 
		
	
		
			
			|  |  |  | <td>{{ truncateWallet(contribution.wallet, 6, undefined) }}</td> | 
		
	
		
			
			|  |  |  | <td>{{ truncateWallet(contribution.wallet, calculateWalletChars(), undefined) }}</td> | 
		
	
		
			
			|  |  |  | <td>{{ contribution.amount }}</td> | 
		
	
		
			
			|  |  |  | <td v-if="!enableConsolidation"> | 
		
	
		
			
			|  |  |  | <span class="transaction-date" :title="formatDate(contribution.createdAt, true)"> | 
		
	
		
			
			|  |  |  | {{ formatDate(contribution.createdAt, true) }} | 
		
	
		
			
			|  |  |  | </span> | 
		
	
		
			
			|  |  |  | </td> | 
		
	
		
			
			|  |  |  | <td v-else> | 
		
	
		
			
			|  |  |  | <span>{{ calculateReward(contribution.amount / fund.fundInfo.price) }}</span> | 
		
	
		
			
			|  |  |  | </td> | 
		
	
		
			
			|  |  |  | <template v-else> | 
		
	
		
			
			|  |  |  | <td> | 
		
	
		
			
			|  |  |  | <span>{{ calculateReward(contribution.amount.div(fund.fundInfo.price)) }}</span> | 
		
	
		
			
			|  |  |  | </td> | 
		
	
		
			
			|  |  |  | <td v-if="store.getters.getToken && hasPermission(Privileges.Admin)"> | 
		
	
		
			
			|  |  |  | <button class="button is-small is-outlined">Send</button> | 
		
	
		
			
			|  |  |  | </td> | 
		
	
		
			
			|  |  |  | </template> | 
		
	
		
			
			|  |  |  | </tr> | 
		
	
		
			
			|  |  |  | </tbody> | 
		
	
		
			
			|  |  |  | </table> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | </section> | 
		
	
		
			
			|  |  |  | <section class="section is-small px-0" | 
		
	
		
			
			|  |  |  | v-if="store.getters.getToken && hasPermission(Privileges.Admin)"> | 
		
	
		
			
			|  |  |  | <div class="title is-size-4 has-text-white-ter has-text-centered"> | 
		
	
		
			
			|  |  |  | Submit Group Fund | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | <div class="box is-flex is-flex-direction-row is-justify-content-space-between"> | 
		
	
		
			
			|  |  |  | <div class="my-auto"> | 
		
	
		
			
			|  |  |  | <label class="checkbox" for="submit-confirm"> | 
		
	
		
			
			|  |  |  | <input type="checkbox" id="submit-confirm" v-model="allowSubmit"> Allow Submit | 
		
	
		
			
			|  |  |  | </label> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | <div> | 
		
	
		
			
			|  |  |  | <button | 
		
	
		
			
			|  |  |  | class="button is-success" | 
		
	
		
			
			|  |  |  | :disabled="!allowSubmit" | 
		
	
		
			
			|  |  |  | @click="submitFund" | 
		
	
		
			
			|  |  |  | > | 
		
	
		
			
			|  |  |  | Submit | 
		
	
		
			
			|  |  |  | </button> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | </section> | 
		
	
		
			
			|  |  |  | <section v-if="store.getters.getToken && hasPermission(Privileges.AdminPlus)"> | 
		
	
		
			
			|  |  |  | <div class="title is-size-4 has-text-white-ter has-text-centered"> | 
		
	
		
			
			|  |  |  | Close Group Fund | 
		
	
	
		
			
				|  |  | @@ -172,7 +186,7 @@ | 
		
	
		
			
			|  |  |  | <div class="box is-flex is-flex-direction-row is-justify-content-space-between"> | 
		
	
		
			
			|  |  |  | <div class="my-auto"> | 
		
	
		
			
			|  |  |  | <label class="checkbox" for="delete-confirm"> | 
		
	
		
			
			|  |  |  | <input type="checkbox" id="delete-confirm" v-model="allowDelete"> Close | 
		
	
		
			
			|  |  |  | <input type="checkbox" id="delete-confirm" v-model="allowDelete"> Allow Close | 
		
	
		
			
			|  |  |  | </label> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | <div> | 
		
	
	
		
			
				|  |  | @@ -207,7 +221,10 @@ import { | 
		
	
		
			
			|  |  |  | ref, | 
		
	
		
			
			|  |  |  | watch, | 
		
	
		
			
			|  |  |  | } from 'vue'; | 
		
	
		
			
			|  |  |  | import { useWebSocket } from '@vueuse/core'; | 
		
	
		
			
			|  |  |  | import { | 
		
	
		
			
			|  |  |  | useWebSocket, | 
		
	
		
			
			|  |  |  | useWindowSize, | 
		
	
		
			
			|  |  |  | } from '@vueuse/core'; | 
		
	
		
			
			|  |  |  | import store from '@/store'; | 
		
	
		
			
			|  |  |  | import { | 
		
	
		
			
			|  |  |  | sanitize, | 
		
	
	
		
			
				|  |  | @@ -222,7 +239,9 @@ import { | 
		
	
		
			
			|  |  |  | getBalance, | 
		
	
		
			
			|  |  |  | getContributions, | 
		
	
		
			
			|  |  |  | getRewardFund, | 
		
	
		
			
			|  |  |  | submitRewardFund, | 
		
	
		
			
			|  |  |  | } from '@/api/composed'; | 
		
	
		
			
			|  |  |  | import Decimal from 'decimal.js'; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const route = useRoute(); | 
		
	
		
			
			|  |  |  | const router = useRouter(); | 
		
	
	
		
			
				|  |  | @@ -238,10 +257,23 @@ const formatDate = (time: string, includeTime = false) => { | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const pk = ref(''); | 
		
	
		
			
			|  |  |  | const amt = ref(undefined as number | undefined); | 
		
	
		
			
			|  |  |  | const amt = ref(undefined as Decimal | undefined); | 
		
	
		
			
			|  |  |  | const selectableDates = ref([undefined] as (string | undefined)[]); | 
		
	
		
			
			|  |  |  | const selectedDate = ref(undefined as string | undefined); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const { width } = useWindowSize(); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const amount = computed({ | 
		
	
		
			
			|  |  |  | get: () => amt.value, | 
		
	
		
			
			|  |  |  | set: (v) => { | 
		
	
		
			
			|  |  |  | if (v) { | 
		
	
		
			
			|  |  |  | amt.value = new Decimal(v); | 
		
	
		
			
			|  |  |  | } else { | 
		
	
		
			
			|  |  |  | amt.value = undefined; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const allowDelete = ref(false); | 
		
	
		
			
			|  |  |  | const deleteFund = async () => { | 
		
	
		
			
			|  |  |  | const deleted = await deleteRewardFund(identifier, allowDelete.value); | 
		
	
	
		
			
				|  |  | @@ -249,6 +281,32 @@ const deleteFund = async () => { | 
		
	
		
			
			|  |  |  | await router.push('/'); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | const delTimeout = ref(undefined as number | undefined); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | watch(allowDelete, () => { | 
		
	
		
			
			|  |  |  | if (delTimeout.value) window.clearTimeout(delTimeout.value); | 
		
	
		
			
			|  |  |  | delTimeout.value = window.setTimeout(() => { | 
		
	
		
			
			|  |  |  | allowDelete.value = false; | 
		
	
		
			
			|  |  |  | delTimeout.value = undefined; | 
		
	
		
			
			|  |  |  | }, 10000); | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const allowSubmit = ref(false); | 
		
	
		
			
			|  |  |  | const submitFund = async () => { | 
		
	
		
			
			|  |  |  | const submitted = await submitRewardFund(identifier, allowSubmit.value); | 
		
	
		
			
			|  |  |  | if (submitted && submitted.success) { | 
		
	
		
			
			|  |  |  | console.log('submitted'); // TODO: provide feedback for submission | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | const subTimeout = ref(undefined as number | undefined); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | watch(allowSubmit, () => { | 
		
	
		
			
			|  |  |  | if (subTimeout.value) window.clearTimeout(subTimeout.value); | 
		
	
		
			
			|  |  |  | subTimeout.value = window.setTimeout(() => { | 
		
	
		
			
			|  |  |  | allowSubmit.value = false; | 
		
	
		
			
			|  |  |  | subTimeout.value = undefined; | 
		
	
		
			
			|  |  |  | }, 10000); | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const enableConsolidation = ref(false); | 
		
	
		
			
			|  |  |  | const fund = ref( | 
		
	
	
		
			
				|  |  | @@ -278,11 +336,11 @@ fundDetails.value = [ | 
		
	
		
			
			|  |  |  | val: fund.value.fundInfo.asset, | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | title: 'Min', | 
		
	
		
			
			|  |  |  | title: 'Minimum', | 
		
	
		
			
			|  |  |  | val: `${fund.value.fundInfo.minContribution.toLocaleString()}`, | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | title: 'Goal', | 
		
	
		
			
			|  |  |  | title: 'Remaining', | 
		
	
		
			
			|  |  |  | val: `${fund.value.fundInfo.amountAvailable.toLocaleString()}`, | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | { | 
		
	
	
		
			
				|  |  | @@ -296,29 +354,47 @@ if (fund.value.contributions.dates) { | 
		
	
		
			
			|  |  |  | ); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const reward = ref(0); | 
		
	
		
			
			|  |  |  | const processContributions = (contributions: Contribution[]) => contributions.map((c) => ({ | 
		
	
		
			
			|  |  |  | ...c, | 
		
	
		
			
			|  |  |  | amount: new Decimal(c.amount), | 
		
	
		
			
			|  |  |  | })); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const reward = ref(new Decimal(0)); | 
		
	
		
			
			|  |  |  | const maxBonus = ref(0); | 
		
	
		
			
			|  |  |  | const bonus = ref(undefined as Bonus | undefined); | 
		
	
		
			
			|  |  |  | const amountHeld = ref(fund.value.total.amountHeld); | 
		
	
		
			
			|  |  |  | const amountAvailable = ref(fund.value.fundInfo.amountAvailable); | 
		
	
		
			
			|  |  |  | const contributions: Ref<Contribution[]> = ref(fund.value.contributions.list ?? []); | 
		
	
		
			
			|  |  |  | const amountHeld = ref(new Decimal(fund.value.total.amountHeld)); | 
		
	
		
			
			|  |  |  | const amountAvailable = ref(new Decimal(fund.value.fundInfo.amountAvailable)); | 
		
	
		
			
			|  |  |  | const contributions: Ref<Contribution[]> = ref(processContributions( | 
		
	
		
			
			|  |  |  | fund.value.contributions.list ?? [], | 
		
	
		
			
			|  |  |  | )); | 
		
	
		
			
			|  |  |  | const offset = ref(contributions.value.length); | 
		
	
		
			
			|  |  |  | const total = ref(fund.value.contributions.total); | 
		
	
		
			
			|  |  |  | const contributionsLoading = ref(false); | 
		
	
		
			
			|  |  |  | const acctBalance = ref(undefined as number | undefined); | 
		
	
		
			
			|  |  |  | const acctBalance = ref(undefined as Decimal | undefined); | 
		
	
		
			
			|  |  |  | const unknownAcct = ref(true); | 
		
	
		
			
			|  |  |  | const loading = ref({ | 
		
	
		
			
			|  |  |  | contribution: false, | 
		
	
		
			
			|  |  |  | balance: false, | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const round = (num: number, figures = 1) => { | 
		
	
		
			
			|  |  |  | const factor = 10 ** figures; | 
		
	
		
			
			|  |  |  | return Math.round(num * factor) / factor; | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const calcPctComplete = () => round(amountHeld.value.div(amountAvailable.value) | 
		
	
		
			
			|  |  |  | .mul(100) | 
		
	
		
			
			|  |  |  | .toNumber()); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const calculateWalletChars = () => round(width.value / 114, 0); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const hasInvalidValues = () => { | 
		
	
		
			
			|  |  |  | if (!fund.value) throw new Error('Fund was not loaded!'); | 
		
	
		
			
			|  |  |  | return [pk, amt].every((v) => v.value !== undefined && v.value !== '') | 
		
	
		
			
			|  |  |  | && (amt.value === 0 | 
		
	
		
			
			|  |  |  | || amt.value! > amountAvailable.value | 
		
	
		
			
			|  |  |  | || amt.value! < fund.value.fundInfo.minContribution | 
		
	
		
			
			|  |  |  | || (acctBalance.value && amt.value! > acctBalance.value) | 
		
	
		
			
			|  |  |  | return [pk, amount].every((v) => v.value !== undefined && v.value !== '') | 
		
	
		
			
			|  |  |  | && (amount.value!.isZero() | 
		
	
		
			
			|  |  |  | || amount.value! > amountAvailable.value | 
		
	
		
			
			|  |  |  | || amount.value!.lt(fund.value.fundInfo.minContribution) | 
		
	
		
			
			|  |  |  | || (acctBalance.value && amt.value!.gt(acctBalance.value)) | 
		
	
		
			
			|  |  |  | || unknownAcct.value); | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
	
		
			
				|  |  | @@ -339,18 +415,17 @@ const getCurrentBonus = () => { | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const calculateReward = (bought: number) => { | 
		
	
		
			
			|  |  |  | const calculateReward = (bought: Decimal) => { | 
		
	
		
			
			|  |  |  | if (bonus.value) { | 
		
	
		
			
			|  |  |  | if (!bonus.value.percent) throw new Error('Bonus did not have percent for some reason'); | 
		
	
		
			
			|  |  |  | reward.value = bought + bought * (bonus.value.percent / 100); | 
		
	
		
			
			|  |  |  | reward.value = bought.add(bought) | 
		
	
		
			
			|  |  |  | .mul(new Decimal(bonus.value.percent / 100)); | 
		
	
		
			
			|  |  |  | } else { | 
		
	
		
			
			|  |  |  | reward.value = bought; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | return reward.value.toLocaleString(); | 
		
	
		
			
			|  |  |  | }; | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const fixNewlines = (s: string) => s.replace('\n', '<br/>'); | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const errs: SignetError[] = [ | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | text: 'Amount is required', | 
		
	
	
		
			
				|  |  | @@ -358,11 +433,11 @@ const errs: SignetError[] = [ | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | text: 'Amount must be greater than 0', | 
		
	
		
			
			|  |  |  | condition: amt.value && amt.value === 0 && !fund.value.fundInfo.minContribution, | 
		
	
		
			
			|  |  |  | condition: amt.value && amt.value.isZero() && !fund.value.fundInfo.minContribution, | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | text: 'Amount is less than the minimum contribution', | 
		
	
		
			
			|  |  |  | condition: amt.value && amt.value < fund.value.fundInfo.minContribution, | 
		
	
		
			
			|  |  |  | condition: amt.value && amt.value.lt(fund.value.fundInfo.minContribution), | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | text: `Not enough ${fund.value.fundInfo.asset} for sale in ICO`, | 
		
	
	
		
			
				|  |  | @@ -370,7 +445,7 @@ const errs: SignetError[] = [ | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | text: `Not enough XLM to send (${amt.value?.toLocaleString()})`, | 
		
	
		
			
			|  |  |  | condition: amt.value && acctBalance.value && amt.value > acctBalance.value, | 
		
	
		
			
			|  |  |  | condition: amt.value && acctBalance.value && amt.value.gt(acctBalance.value), | 
		
	
		
			
			|  |  |  | }, | 
		
	
		
			
			|  |  |  | { | 
		
	
		
			
			|  |  |  | text: 'Could not find Stellar wallet', | 
		
	
	
		
			
				|  |  | @@ -385,7 +460,7 @@ watch(selectedDate, async (newVal) => { | 
		
	
		
			
			|  |  |  | const conts = await getContributions(identifier, offset.value, newVal, enableConsolidation.value); | 
		
	
		
			
			|  |  |  | if (!fund.value) throw new Error('Fund not found'); | 
		
	
		
			
			|  |  |  | if (!conts) throw new Error('Contributions not found'); | 
		
	
		
			
			|  |  |  | contributions.value = conts.list; | 
		
	
		
			
			|  |  |  | contributions.value = processContributions(conts.list); | 
		
	
		
			
			|  |  |  | offset.value = contributions.value.length; | 
		
	
		
			
			|  |  |  | total.value = fund.value.contributions.total; | 
		
	
		
			
			|  |  |  | }); | 
		
	
	
		
			
				|  |  | @@ -407,7 +482,7 @@ const loadMoreIfNeeded = async (e: Event) => { | 
		
	
		
			
			|  |  |  | if (!moreContribs) throw new Error('Contributions not found'); | 
		
	
		
			
			|  |  |  | offset.value += moreContribs.list.length; | 
		
	
		
			
			|  |  |  | total.value = moreContribs.total; | 
		
	
		
			
			|  |  |  | contributions.value = contributions.value.concat(moreContribs.list); | 
		
	
		
			
			|  |  |  | contributions.value = contributions.value.concat(processContributions(moreContribs.list)); | 
		
	
		
			
			|  |  |  | contributionsLoading.value = false; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }; | 
		
	
	
		
			
				|  |  | @@ -432,9 +507,9 @@ const queryAccount = async () => { | 
		
	
		
			
			|  |  |  | acctBalance.value = undefined; | 
		
	
		
			
			|  |  |  | } else { | 
		
	
		
			
			|  |  |  | unknownAcct.value = false; | 
		
	
		
			
			|  |  |  | if (resp?.balance) { | 
		
	
		
			
			|  |  |  | acctBalance.value = resp?.balance; | 
		
	
		
			
			|  |  |  | if (amt.value && amt.value > acctBalance.value) { | 
		
	
		
			
			|  |  |  | if (resp && resp.balance) { | 
		
	
		
			
			|  |  |  | acctBalance.value = new Decimal(resp.balance); | 
		
	
		
			
			|  |  |  | if (amt.value && amt.value.gt(acctBalance.value)) { | 
		
	
		
			
			|  |  |  | amt.value = acctBalance.value; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | } | 
		
	
	
		
			
				|  |  | @@ -445,12 +520,13 @@ const queryAccount = async () => { | 
		
	
		
			
			|  |  |  | 
 | 
		
	
		
			
			|  |  |  | const makeContribution = async () => { | 
		
	
		
			
			|  |  |  | if (!fund.value) throw new Error('Fund not found'); | 
		
	
		
			
			|  |  |  | if (!amt.value) return; | 
		
	
		
			
			|  |  |  | if (!/[^[0-9]+$/.test(amt.value.toString())) return; | 
		
	
		
			
			|  |  |  | if (!amount.value) return; | 
		
	
		
			
			|  |  |  | if (!/^[0-9]+$/.test(amount.value.toString())) return; | 
		
	
		
			
			|  |  |  | if (unknownAcct.value) return; | 
		
	
		
			
			|  |  |  | if (!loading.value.contribution && pk.value && amt.value && amt.value <= amountAvailable.value) { | 
		
	
		
			
			|  |  |  | if (!loading.value.contribution && pk.value | 
		
	
		
			
			|  |  |  | && amount.value && amount.value <= amountAvailable.value) { | 
		
	
		
			
			|  |  |  | loading.value.contribution = true; | 
		
	
		
			
			|  |  |  | await contribute(sanitize(pk.value), amt.value, fund.value.fundInfo.id); | 
		
	
		
			
			|  |  |  | await contribute(sanitize(pk.value), amount.value!.toNumber(), fund.value.fundInfo.id); | 
		
	
		
			
			|  |  |  | loading.value.contribution = false; | 
		
	
		
			
			|  |  |  | pk.value = ''; | 
		
	
		
			
			|  |  |  | amt.value = undefined; | 
		
	
	
		
			
				|  |  | @@ -467,7 +543,7 @@ watch(enableConsolidation, async () => { | 
		
	
		
			
			|  |  |  | ); | 
		
	
		
			
			|  |  |  | if (!fund.value) throw new Error('Fund not found'); | 
		
	
		
			
			|  |  |  | if (!conts) throw new Error('Contributions not found'); | 
		
	
		
			
			|  |  |  | contributions.value = conts.list; | 
		
	
		
			
			|  |  |  | contributions.value = processContributions(conts.list); | 
		
	
		
			
			|  |  |  | offset.value = contributions.value.length; | 
		
	
		
			
			|  |  |  | total.value = fund.value.contributions.total; | 
		
	
		
			
			|  |  |  | }); | 
		
	
	
		
			
				|  |  | @@ -488,13 +564,13 @@ watch(data, (newVal) => { | 
		
	
		
			
			|  |  |  | .includes(v.wallet)) { | 
		
	
		
			
			|  |  |  | const hasContribution = contributions.value.find((c: Contribution) => c.wallet === v.wallet); | 
		
	
		
			
			|  |  |  | if (!hasContribution) throw new Error('Something went wrong'); | 
		
	
		
			
			|  |  |  | hasContribution.amount += v.amount; | 
		
	
		
			
			|  |  |  | hasContribution.amount = new Decimal(hasContribution.amount).add(v.amount); | 
		
	
		
			
			|  |  |  | } else { | 
		
	
		
			
			|  |  |  | contributions.value.splice(0, 0, v); | 
		
	
		
			
			|  |  |  | offset.value += 1; | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | amountHeld.value += v.amount; | 
		
	
		
			
			|  |  |  | amountAvailable.value -= v.amount; | 
		
	
		
			
			|  |  |  | amountHeld.value = new Decimal(amountHeld.value).add(v.amount); | 
		
	
		
			
			|  |  |  | amountAvailable.value = new Decimal(amountAvailable.value).sub(v.amount); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | </script> | 
		
	
	
		
			
				|  |  | 
 |