Skip to content

Withdraw and deposit functionality #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
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
2 changes: 1 addition & 1 deletion blockchain/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
FROM ghcr.io/foundry-rs/foundry
ENTRYPOINT [ "anvil", "--block-time", "5", "--host", "0.0.0.0" ]
ENTRYPOINT [ "anvil", "--block-time", "1", "--host", "0.0.0.0" ]
103 changes: 103 additions & 0 deletions web/apps/processor/src/components/Asset/DepositCore.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Show, createEffect, createSignal } from "solid-js";
import { useSession } from "@packages/components/providers/SessionProvider";
import { Asset } from "@packages/types/asset";
import {
Fraction,
ev,
fFromBigint,
fmul,
} from "@packages/types/primitives/fraction";
import { DepositTextInput } from "../Inputs/DepositTextInput";
import { useWallet } from "@packages/components/providers/WalletProvider";
import {
ADDRESS,
ABI as TREASURY_ABI,
ADDRESS as TREASURY_ADDRESS,
} from "../../../../../packages/contracts/treasury";
import { ABI as ERC20_ABI } from "../../../../../packages/contracts/erc20";
import { Address } from "viem";
import { DepositNumberInput } from "../Inputs/DepositNumberInput";
import { handleDeposit } from "~/helpers/handleDeposit";

export function CreateProccessorDeposit(asset?: Asset, precision?: number) {
return () => (
<Show when={asset && precision}>
<DepositCore asset={asset!} precision={precision!} />
</Show>
);
}

const splitSig = (sig: string) => {
const pureSig = sig.replace("0x", "");
const r = "0x" + pureSig.substring(0, 64);
const s = "0x" + pureSig.substring(64, 128);
const v = parseInt(pureSig.substring(128, 130), 16);
return {
r,
s,
v,
};
};

export function DepositCore(props: { asset: Asset; precision: number }) {
const [amount, setAmount] = createSignal<Fraction>(fFromBigint(0n));
const session = useSession();
const wallet = useWallet();
const [address, setAddress] = createSignal<Address | undefined>(
wallet.address
);

createEffect(() => {
setAddress(wallet.address);
});

createEffect(() => {});

return (
<>
<div class="flex flex-col items-center justify-start gap-3">
<DepositNumberInput
precision={props.precision}
left={"Quantity"}
right={props.asset.symbol}
value={amount()}
onChange={(f) => {
setAmount(f);
}}
/>
<DepositTextInput
left={"Deposit to"}
value={address()}
onChange={(f) => {
setAddress(f.toLowerCase() as Address);
}}
/>
<div
class={`grid py-3 mt-2 w-full
${
session()
? "cursor-pointer bg-ksox-2 active:bg-opacity-70"
: "bg-gray-3"
}
select-none items-center justify-center rounded-lg text-lg transition-colors duration-75
`}
onClick={async () => {
try {
await handleDeposit({
asset: props.asset,
address_value: address(),
amount: amount(),
treasury_address: ADDRESS,
wallet: wallet,
});
} catch (error) {
console.error("Error during deposit:", error);
}
}}
>
Deposit
</div>
</div>
</>
);
}
104 changes: 104 additions & 0 deletions web/apps/processor/src/components/Asset/WithdrawCore.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Show, createEffect, createSignal } from "solid-js";
import { useSession } from "@packages/components/providers/SessionProvider";
import { Asset } from "@packages/types/asset";
import {
Fraction,
ev,
fFromBigint,
fmul,
} from "@packages/types/primitives/fraction";
import { DepositTextInput } from "../Inputs/DepositTextInput";
import { useWallet } from "@packages/components/providers/WalletProvider";
import {
ABI as TREASURY_ABI,
ADDRESS as TREASURY_ADDRESS,
} from "../../../../../packages/contracts/treasury";
import { ABI as ERC20_ABI } from "../../../../../packages/contracts/erc20";
import { Address } from "viem";
import { DepositNumberInput } from "../Inputs/DepositNumberInput";
import { WithdrawRequest, WithdrawResponse } from "@packages/types/mod";
import { api } from "~/root";
import { handleWithdraw } from "~/helpers/handleWithdraw";

export function CreateProccessorWithdraw(asset?: Asset, precision?: number) {
return () => (
<Show when={asset && precision}>
<WithdrawCore asset={asset!} precision={precision!} />
</Show>
);
}

const splitSig = (sig: string) => {
const pureSig = sig.replace("0x", "");
const r = "0x" + pureSig.substring(0, 64);
const s = "0x" + pureSig.substring(64, 128);
const v = parseInt(pureSig.substring(128, 130), 16);
return {
r,
s,
v,
};
};

export function WithdrawCore(props: { asset: Asset; precision: number }) {
const [amount, setAmount] = createSignal<Fraction>(fFromBigint(0n));
const session = useSession();
const wallet = useWallet();
const [address, setAddress] = createSignal<Address | undefined>(
wallet.address
);

createEffect(() => {
setAddress(wallet.address);
});

createEffect(() => {});

return (
<>
<div class="flex flex-col items-center justify-start gap-3">
<DepositNumberInput
precision={props.precision}
left={"Quantity"}
right={props.asset.symbol}
value={amount()}
onChange={(f) => {
setAmount(f);
}}
/>
<DepositTextInput
left={"Deposit to"}
value={address()}
onChange={(f) => {
setAddress(f.toLowerCase() as Address);
}}
/>
<div
class={`grid py-3 mt-2 w-full
${
session()
? "cursor-pointer bg-ksox-2 active:bg-opacity-70"
: "bg-gray-3"
}
select-none items-center justify-center rounded-lg text-lg transition-colors duration-75
`}
onClick={async () => {
try {
await handleWithdraw({
asset: props.asset,
address_value: address(),
amount: amount(),
treasury_address: TREASURY_ADDRESS,
wallet: wallet,
});
} catch (error) {
console.error("Error during deposit:", error);
}
}}
>
Withdraw
</div>
</div>
</>
);
}
178 changes: 178 additions & 0 deletions web/apps/processor/src/components/Deposit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import {
Component,
Show,
createEffect,
createMemo,
createSignal,
onCleanup,
} from "solid-js";
import { useAssets } from "./providers/AssetsProvider";
import { Asset } from "@packages/types/asset";
import { joinPaths } from "solid-start/islands/server-router";
import { api, base } from "~/root";
import { AVAILABLE_CHAINS } from "@packages/components/providers/WalletProvider/chains";
import { unwrap } from "solid-js/store";
import { useWallet } from "@packages/components/providers/WalletProvider";
import { CreateProccessorDeposit } from "./Asset/DepositCore";
import { usePrecision } from "@packages/components/providers/PrecisionProvider";
import { Dynamic } from "solid-js/web";
import { Fraction, ev } from "@packages/types/primitives/fraction";
import { useSession } from "@packages/components/providers/SessionProvider";
import subscribeEvents from "@packages/utils/subscribeEvents";
import params from "@packages/utils/params";
import { Valut } from "@packages/types/valut";
import { format } from "numerable";
import { formatTemplate } from "@packages/utils/precision";

export const Deposit: Component<{}> = ({}) => {
const assets = useAssets();
const wallet = useWallet();
const session = useSession();
const precision = usePrecision();
const assetsList = createMemo(() => [...assets().values()]);

const [selectedAsset, setSelectedAsset] = createSignal<Asset | undefined>();

const assetMap = createMemo(() => {
const map = new Map();
assetsList().forEach((asset) => map.set(asset.id, asset));
return map;
});

const handleChangeCoin = (event: Event) => {
const assetId = (event.target as HTMLSelectElement).value;
setSelectedAsset(assetMap().get(assetId));
};

createEffect(() => {
if (assetsList().length > 0) {
setSelectedAsset(assetsList()[0]);
}
});

const [balance, setBalance] = createSignal<Fraction | undefined>(undefined);

let eventsource: EventSource | undefined;

createEffect(async () => {
if (session() && selectedAsset() && precision()) {
const asset = selectedAsset();

eventsource = await subscribeEvents(
`${api}/private/balance`,
params({ asset_id: asset!.id }),
params({ asset_id: asset!.id }),
(data) => {
setBalance(Valut.parse(data).balance.Finite);
}
);
}
});

onCleanup(() => {
eventsource?.close();
});

return (
<div class="p-4">
<h1 class="text-2xl font-semibold mb-6 text-center">Deposit funds</h1>
<div class="flex gap-4 items-center mb-4">
<div class="relative inline-block w-full">
<select
class="block appearance-none w-full bg-gray-2 text-white border-gray-400 hover:border-gray-500 px-6 py-5 pr-8 rounded-xl shadow leading-tight focus:outline-none focus:shadow-outline"
id="grid-state"
onChange={handleChangeCoin}
>
{AVAILABLE_CHAINS.map((network) => (
<option
onClick={async () => {
try {
await wallet.walletClient?.addChain({
chain: unwrap(network.network),
});
} catch (error) {
console.log(error);
}

try {
await wallet.walletClient?.switchChain({
id: unwrap(network.network).id,
});
} catch (error) {
console.log(error);
}
}}
>
{network.network.name}
</option>
))}
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-6 text-gray-700">
<svg
class="fill-current h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
>
<path d="M10 12.586L2.929 5.515c-.39-.39-1.023-.39-1.414 0-.39.39-.39 1.023 0 1.414l8 8c.39.39 1.023.39 1.414 0l8-8c.39-.39.39-1.023 0-1.414-.39-.39-1.023-.39-1.414 0L10 12.586z" />
</svg>
</div>
</div>
<Show when={selectedAsset()}>
<img
src={joinPaths(base, wallet.selected_network.icon)}
width="36px"
height="36px"
/>
</Show>
</div>
{/* */}
<div class="flex gap-4 items-center mb-2">
<div class="relative inline-block w-full">
<select
class="block appearance-none w-full bg-gray-2 text-white border-gray-400 hover:border-gray-500 px-6 py-5 pr-8 rounded-xl shadow leading-tight focus:outline-none focus:shadow-outline"
id="grid-state"
onChange={handleChangeCoin}
>
{assetsList().map((asset) => (
<option value={asset.id}>{asset.symbol}</option>
))}
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-6 text-gray-700">
<svg
class="fill-current h-4 w-4"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
>
<path d="M10 12.586L2.929 5.515c-.39-.39-1.023-.39-1.414 0-.39.39-.39 1.023 0 1.414l8 8c.39.39 1.023.39 1.414 0l8-8c.39-.39.39-1.023 0-1.414-.39-.39-1.023-.39-1.414 0L10 12.586z" />
</svg>
</div>
</div>
<Show when={selectedAsset()}>
<img
src={joinPaths(
base,
"/gfx/asset_icons/" +
(selectedAsset() as Asset).symbol.toLowerCase() +
".svg"
)}
width="36px"
height="36px"
/>
</Show>
</div>
<Show when={selectedAsset() && precision()}>
<p class="text-sm text-gray-4 mb-6">
{balance() != undefined
? `Your balance: ${format(
ev(balance()!),
formatTemplate(precision() ?? 3)
)} ${selectedAsset()?.symbol}`
: "Your balance: "}
</p>
</Show>
<Dynamic
component={CreateProccessorDeposit(selectedAsset(), precision())}
/>
</div>
);
};
Loading