Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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" ]
164 changes: 164 additions & 0 deletions web/apps/processor/src/components/Asset/DepositCore.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
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";

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 () => {
const address_value = address();
const value = BigInt(
Math.floor(ev(fmul(props.asset.decimals, amount())))
);
if (wallet && address_value && wallet.address) {
const nonce = (await wallet.publicClient?.readContract({
address: props.asset.address as Address,
abi: ERC20_ABI,
functionName: "nonces",
account: wallet.address as Address,
args: [wallet.address as Address],
})) as bigint;

const deadline =
((await wallet.publicClient?.getBlock())?.timestamp ?? 0n) +
3600n;

const domain = {
name: props.asset.name,
version: "1",
chainId: BigInt(wallet.selected_network.network.id),
verifyingContract: props.asset.address as Address,
};

const permit = {
owner: wallet.address as Address,
spender: TREASURY_ADDRESS as Address,
value,
nonce,
deadline,
};

console.log(domain, permit);

const signature = await wallet.walletClient?.signTypedData({
account: wallet.address as Address,
domain,
types: {
Permit: [
{ name: "owner", type: "address" },
{ name: "spender", type: "address" },
{ name: "value", type: "uint256" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
],
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "verifyingContract", type: "address" },
],
},
primaryType: "Permit",
message: permit,
});
if (signature) {
const { r, s, v } = splitSig(signature);
await wallet.walletClient?.writeContract({
chain: wallet.selected_network.network,
address: TREASURY_ADDRESS,
abi: TREASURY_ABI,
functionName: "depositPermit",
account: wallet.address as Address,
args: [
props.asset.address as Address,
value,
deadline,
v,
r,
s,
],
});
}
}
}}
>
Deposit
</div>
</div>
</>
);
}
180 changes: 180 additions & 0 deletions web/apps/processor/src/components/Deposit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
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())}
/>
{selectedAsset() && selectedAsset()!.name}
{/* */}
</div>
);
};
Loading