Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 76 additions & 1 deletion src/lib/components/Portfolio.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
import McIcon from "./MCIcon.svelte";
import LoadingLambo from "./memecooking/Board/LoadingLambo.svelte";
import SendSheet from "./memecooking/BottomSheet/SendSheet.svelte";
import WrappingSheet from "./memecooking/BottomSheet/WrappingSheet.svelte";

import Near from "$lib/assets/Near.svelte";
import { openBottomSheet } from "$lib/layout/BottomSheet/Container.svelte";
import { refreshNearBalance, nearBalance, wallet } from "$lib/near";
import { refreshNearBalance, refreshWrappedNearBalance, nearBalance, wrappedNearBalance, wallet } from "$lib/near";
import type { Portfolio } from "$lib/store/portfolio";
import { getNearPrice, nearPrice } from "$lib/util/projectedMCap";

Expand All @@ -19,10 +20,12 @@
$: isOwnAccount = accountId === get(wallet.accountId$);

refreshNearBalance(accountId);
refreshWrappedNearBalance(accountId);
getNearPrice();

const refreshInterval = setInterval(() => {
refreshNearBalance(accountId);
refreshWrappedNearBalance(accountId);
getNearPrice();
}, 30e3);

Expand Down Expand Up @@ -84,6 +87,78 @@
totalDigits={6}
/>
</td>
{#if isOwnAccount}
<td
class="hidden sm:flex py-3 text-right flex-col items-center gap-1"
>
<button
class="px-1 py-1 bg-shitzu-4 hover:bg-shitzu-5 text-white rounded-md text-sm flex flex-col items-center gap-1"
on:click={() => {
openBottomSheet(WrappingSheet, { walletConnected: isOwnAccount,
defaultTab : "Wrap",
UpdateBalances: () => {
refreshNearBalance(accountId);
refreshWrappedNearBalance(accountId);
}
});
}}
>
<div class="i-mdi:arrow-right" />
</button>
<span class="text-xs text-gray-400">Wrap</span>
</td>
{/if}
</tr>
{/if}
<!-- Wrapped NEAR (wNEAR) -->
{#if $wrappedNearBalance}
<tr class="bg-gray-800/20 hover:bg-gray-800/50 transition-colors">
<td class="px-4 py-3 truncate max-w-[200px]">
<div class="flex items-center gap-3">
<Near className="w-8 h-8 rounded-full bg-white text-black p-1" />
<div class="flex flex-col">
<span class="font-medium">wNEAR</span>
<span class="text-xs text-gray-400 sm:hidden">
$<FormatNumber number={Number($nearPrice) / 1e24} totalDigits={6} />
</span>
</div>
</div>
</td>
<td class="px-4 py-3 text-right">
<div class="flex flex-col items-end">
<span class="font-medium">{$wrappedNearBalance.format()}</span>
<span class="text-xs text-gray-400">
${(($wrappedNearBalance.toNumber() * Number($nearPrice)) / 1e24).toFixed(2)}
</span>
</div>
</td>
<td class="hidden sm:table-cell px-4 py-3 text-right font-medium">
${(Number($nearPrice) / 1e24).toFixed(10)}
</td>
<td class="hidden sm:table-cell px-4 py-3 text-right font-medium">
$<FormatNumber number={(Number($nearPrice) / 1e24) * 1e9} totalDigits={6} />
</td>
{#if isOwnAccount}
<td
class="hidden sm:flex py-3 text-right flex-col items-center gap-1"
>
<button
class="px-1 py-1 bg-shitzu-4 hover:bg-shitzu-5 text-white rounded-md text-sm flex flex-col items-center gap-1"
on:click={() => {
openBottomSheet(WrappingSheet, { walletConnected: isOwnAccount,
defaultTab : "Unwrap",
UpdateBalances: () => {
refreshNearBalance(accountId);
refreshWrappedNearBalance(accountId);
}
});
}}
>
<div class="i-mdi:arrow-right" />
</button>
<span class="text-xs text-gray-400">Unwrap</span>
</td>
{/if}
</tr>
{/if}
{#each portfolio.tokens
Expand Down
153 changes: 153 additions & 0 deletions src/lib/components/memecooking/BottomSheet/WrappingSheet.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<script lang="ts">
import { writable } from "svelte/store";
import { BottomSheetContent } from "$lib/layout/BottomSheet";
import { wallet, type TransactionCallbacks, nearBalance, wrappedNearBalance } from "$lib/near";
import Near from "$lib/assets/Near.svelte";
import { createTabs, melt } from "@melt-ui/svelte";
import { crossfade, fade, slide } from "svelte/transition";
import { match } from "ts-pattern";
import { TokenInput, Button } from "$lib/components";
import { FixedNumber } from "$lib/util";

export let walletConnected: boolean;
export let defaultTab: writable<string>;
export let UpdateBalances: () => void;

const [send, receive] = crossfade({
duration: 300,
});

const {
elements: { list, trigger },
states: { value },
} = createTabs({
defaultValue: defaultTab,
});

let tabs: { label: "Wrap" | "Unwrap" }[] = [
{
label: "Wrap",
},
{
label: "Unwrap",
},
];
$: active = { label: $value } as (typeof tabs)[number];

let input: TokenInput;
let inputValue$ = writable<string | undefined>();
$: input$ = input?.u128$;

$: if (active) {
$inputValue$ = "";
}

let disabled = true;
$: if ($input$?.valueOf() === 0n) {
disabled = true;
} else {
disabled = false;
}

function setMax() {
$inputValue$ = match(active.label)
.with("Wrap", () => $nearBalance.sub(new FixedNumber(2n, 1)).toString())
.with("Unwrap", () => Number($wrappedNearBalance).toFixed(24))
.exhaustive();
}

async function handleWrappingButton() {
const transactions: HereCall[] = [];

const methodName = active.label === "Wrap" ? "near_deposit" : "near_withdraw";
const args = methodName === "near_deposit"
? {}
: { amount: $input$.toU128() };

transactions.push({
receiverId: import.meta.env.VITE_WRAP_NEAR_CONTRACT_ID,
actions: [
{
type: "FunctionCall",
params: {
methodName: methodName,
args,
gas: 30_000_000_000_000n.toString(),
deposit: methodName === "near_deposit" ? $input$.toU128() : "1",
},
},
],
});
// Envia a transação
await wallet.signAndSendTransactions(
{ transactions },
{
onSuccess: () => {
UpdateBalances();
$inputValue$ = "";
},
onFinally: () => {},
},
);
}

</script>

<BottomSheetContent variant="shitzu">
<div
transition:slide
use:melt={$list}
class="flex shrink-0 justify-evenly overflow-x-auto text-white font-bold text-xl mb-5"
aria-label="Manage your account"
>
{#each tabs as { label }}
<button use:melt={$trigger(label)} class="trigger relative first">
{#if $value === label}
<div
in:send={{ key: "trigger" }}
out:receive={{ key: "trigger" }}
class="absolute bottom-0 left-1/2 h-1 w-[120%] -translate-x-1/2 rounded-full bg-lime rounded"
/>
{/if}
{label}
</button>
{/each}
</div>
<div class="tab">
<div
class="py-3 px-3 border border-lime rounded-xl flex justify-between items-center"
>
<Near className="size-8" />
<TokenInput
class="bg-transparent focus:outline-none text-gray-300"
bind:this={input}
bind:value={$inputValue$}
decimals={24}
/>
<button
on:click={setMax}
class="bg-lime/15 text-lime px-4 py-2 rounded-lg text-sm"
>
Max
</button>
</div>
</div>
{#if walletConnected}
<Button
class="w-full py-3 mt-3 text-xl"
onClick={handleWrappingButton}
{disabled}
>
{active.label}
</Button>
{:else}
<slot />
{/if}
<slot slot="header">
<h2
class="prose prose-invert prose-shitzu px-4 text-2xl font-bold text-shitzu-4"
>
Wrapping NEAR
</h2>
</slot>
</BottomSheetContent>
20 changes: 18 additions & 2 deletions src/lib/near/balance.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { writable } from "svelte/store";

import { wallet } from "./wallet";

import { FixedNumber } from "$lib/util";
import { Ft } from "$lib/near";

export const nearBalance = writable<FixedNumber | null>(null);
export const wrappedNearBalance = writable<FixedNumber | null>(null);

export async function refreshNearBalance(accountId?: string): Promise<void> {
if (typeof accountId !== "string") {
Expand Down Expand Up @@ -39,7 +39,23 @@ export async function refreshNearBalance(accountId?: string): Promise<void> {
}
}

export async function refreshWrappedNearBalance(accountId?: string): Promise<void> {
if (typeof accountId !== "string") {
wrappedNearBalance.set(null);
return;
}
try {
const decimals = 24;
const balance = await Ft.balanceOf(import.meta.env.VITE_WRAP_NEAR_CONTRACT_ID, accountId, decimals);
wrappedNearBalance.set(balance);
} catch (error) {
console.error("Error refreshing wNEAR balance:", error);
wrappedNearBalance.set(null);
}
}

wallet.accountId$.subscribe((accountId) => {
if (accountId == null) return;
refreshNearBalance(accountId);
refreshWrappedNearBalance(accountId);
});
9 changes: 5 additions & 4 deletions src/routes/(memecooking)/profile/[...accountId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import MemeList from "$lib/components/memecooking/Profile/MemeList.svelte";
import Revenue from "$lib/components/memecooking/Profile/Revenue.svelte";
import { wallet } from "$lib/near";
import { nearBalance } from "$lib/near/balance";
import { wrappedNearBalance, nearBalance } from "$lib/near/balance";
import {
fetchMcAccount,
mcAccount$,
Expand Down Expand Up @@ -124,14 +124,15 @@
portfolio?.tokens.reduce(
(acc, token) => {
const decimals =
token.contract_id === "wrap.near" ? 24 : token.decimals ?? 18;
token.contract_id === import.meta.env.VITE_WRAP_NEAR_CONTRACT_ID ? 24 : token.decimals ?? 18;
const balance = Number(token.balance) / 10 ** decimals;
const price = token.price
? (token.price * Number($nearPrice)) / 1e24
: 0;
return acc + balance * price;
},
$nearBalance ? ($nearBalance.toNumber() * Number($nearPrice)) / 1e24 : 0,
($nearBalance ? ($nearBalance.toNumber() * Number($nearPrice)) / 1e24 : 0) +
($wrappedNearBalance ? ($wrappedNearBalance.toNumber() * Number($nearPrice)) / 1e24 : 0)
) ?? 0;
</script>

Expand All @@ -143,7 +144,7 @@
Back
</a>

<section class="w-full flex flex-col items-center justify-center px-1">
<section class="w-full flex flex-col items-center justify-center">
<!-- Welcome Banner -->
<div class="w-full bg-gray-800 rounded-lg p-4 sm:p-6 mb-8">
<div
Expand Down