Skip to content
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

Transfer #3981

Open
wants to merge 6 commits into
base: main
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
13 changes: 8 additions & 5 deletions app/src/generated/graphql-env.d.ts

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions app2/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions app2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"postinstall": "patch-package",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "eslint .",
"test:unit": "vitest",
"test": "npm run test:unit -- --run"
},
"devDependencies": {
"@cosmjs/amino": "^0.33.0",
"@cosmjs/cosmwasm-stargate": "0.33.0",
"@cosmjs/proto-signing": "^0.33.0",
"@cosmjs/stargate": "0.33.0",
"@cosmjs/tendermint-rpc": "^0.33.0",
"patch-package": "^8.0.0",
"@effect/platform": "^0.77.2",
"@eslint/compat": "^1.2.6",
"@eslint/js": "^9.20.0",
Expand Down
23 changes: 23 additions & 0 deletions app2/patches/@cosmjs+amino+0.33.0.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
diff --git a/node_modules/@cosmjs/amino/build/pubkeys.js b/node_modules/@cosmjs/amino/build/pubkeys.js
index e9844ef..86101f8 100644
--- a/node_modules/@cosmjs/amino/build/pubkeys.js
+++ b/node_modules/@cosmjs/amino/build/pubkeys.js
@@ -9,6 +9,10 @@ function isSecp256k1Pubkey(pubkey) {
return pubkey.type === "tendermint/PubKeySecp256k1";
}
exports.isSecp256k1Pubkey = isSecp256k1Pubkey;
+function isBn254Pubkey(pubkey) {
+ return pubkey.type === "tendermint/PubKeyBn254";
+}
+exports.isBn254Pubkey = isBn254Pubkey;
exports.pubkeyType = {
/** @see https://github.com/tendermint/tendermint/blob/v0.33.0/crypto/ed25519/ed25519.go#L22 */
secp256k1: "tendermint/PubKeySecp256k1",
@@ -16,6 +20,7 @@ exports.pubkeyType = {
ed25519: "tendermint/PubKeyEd25519",
/** @see https://github.com/tendermint/tendermint/blob/v0.33.0/crypto/sr25519/codec.go#L12 */
sr25519: "tendermint/PubKeySr25519",
+ bn254: "tendermint/PubKeyBn254",
multisigThreshold: "tendermint/PubKeyMultisigThreshold",
};
function isSinglePubkey(pubkey) {
73 changes: 73 additions & 0 deletions app2/patches/@cosmjs+tendermint-rpc+0.33.0.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
diff --git a/node_modules/@cosmjs/tendermint-rpc/build/comet38/adaptor/responses.js b/node_modules/@cosmjs/tendermint-rpc/build/comet38/adaptor/responses.js
index 29ec063..28a5c02 100644
--- a/node_modules/@cosmjs/tendermint-rpc/build/comet38/adaptor/responses.js
+++ b/node_modules/@cosmjs/tendermint-rpc/build/comet38/adaptor/responses.js
@@ -72,7 +72,7 @@ function decodePubkey(data) {
if ("Sum" in data) {
// we don't need to check type because we're checking algorithm
const [[algorithm, value]] = Object.entries(data.Sum.value);
- (0, utils_1.assert)(algorithm === "ed25519" || algorithm === "secp256k1", `unknown pubkey type: ${algorithm}`);
+ (0, utils_1.assert)(algorithm === "ed25519" || algorithm === "secp256k1" || algorithm === "bn254", `unknown pubkey type: ${algorithm}`);
return {
algorithm,
data: (0, encoding_1.fromBase64)((0, encodings_1.assertNotEmpty)(value)),
@@ -91,6 +91,16 @@ function decodePubkey(data) {
algorithm: "secp256k1",
data: (0, encoding_1.fromBase64)((0, encodings_1.assertNotEmpty)(data.value)),
};
+ case "tendermint/PubKeyBn254":
+ return {
+ algorithm: "bn254",
+ data: (0, encoding_1.fromBase64)((0, encodings_1.assertNotEmpty)(data.value)),
+ };
+ case "cometbft/PubKeyBn254":
+ return {
+ algorithm: "bn254",
+ data: (0, encoding_1.fromBase64)((0, encodings_1.assertNotEmpty)(data.value)),
+ };
default:
throw new Error(`unknown pubkey type: ${data.type}`);
}
diff --git a/node_modules/@cosmjs/tendermint-rpc/build/tendermint37/adaptor/responses.js b/node_modules/@cosmjs/tendermint-rpc/build/tendermint37/adaptor/responses.js
index 19df9de..0015044 100644
--- a/node_modules/@cosmjs/tendermint-rpc/build/tendermint37/adaptor/responses.js
+++ b/node_modules/@cosmjs/tendermint-rpc/build/tendermint37/adaptor/responses.js
@@ -72,7 +72,7 @@ function decodePubkey(data) {
if ("Sum" in data) {
// we don't need to check type because we're checking algorithm
const [[algorithm, value]] = Object.entries(data.Sum.value);
- (0, utils_1.assert)(algorithm === "ed25519" || algorithm === "secp256k1", `unknown pubkey type: ${algorithm}`);
+ (0, utils_1.assert)(algorithm === "ed25519" || algorithm === "secp256k1" || algorithm === "bn254", `unknown pubkey type: ${algorithm}`);
return {
algorithm,
data: (0, encoding_1.fromBase64)((0, encodings_1.assertNotEmpty)(value)),
@@ -91,6 +91,16 @@ function decodePubkey(data) {
algorithm: "secp256k1",
data: (0, encoding_1.fromBase64)((0, encodings_1.assertNotEmpty)(data.value)),
};
+ case "tendermint/PubKeyBn254":
+ return {
+ algorithm: "bn254",
+ data: (0, encoding_1.fromBase64)((0, encodings_1.assertNotEmpty)(data.value)),
+ };
+ case "cometbft/PubKeyBn254":
+ return {
+ algorithm: "bn254",
+ data: (0, encoding_1.fromBase64)((0, encodings_1.assertNotEmpty)(data.value)),
+ };
default:
throw new Error(`unknown pubkey type: ${data.type}`);
}
diff --git a/node_modules/@cosmjs/tendermint-rpc/build/tendermintclient.js b/node_modules/@cosmjs/tendermint-rpc/build/tendermintclient.js
index 257b104..dbf2240 100644
--- a/node_modules/@cosmjs/tendermint-rpc/build/tendermintclient.js
+++ b/node_modules/@cosmjs/tendermint-rpc/build/tendermintclient.js
@@ -28,7 +28,7 @@ async function connectComet(endpoint) {
if (version.startsWith("0.37.")) {
out = tm37Client;
}
- else if (version.startsWith("0.38.")) {
+ else if (version.startsWith("0.38.") || version.startsWith("1.0.")) {
tm37Client.disconnect();
out = await comet38_1.Comet38Client.connect(endpoint);
}
35 changes: 35 additions & 0 deletions app2/src/lib/components/Transfer/Amount.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script lang="ts">
import Input from "$lib/components/ui/Input.svelte";
import {getTransfer} from "../../../routes/transfer/transfer.svelte.js";

const {transfer} = getTransfer()
</script>
<Input id="amount"
label="amount"
type="text"
required={true}
disabled={!transfer.raw.asset}
autocorrect="off"
placeholder="0.00"
spellcheck="false"
autocomplete="off"
inputmode="decimal"
data-field="amount"
autocapitalize="none"
pattern="^[0-9]*[.]?[0-9]*$"
value={transfer.raw.amount}
oninput={(event) => {
const input = event.currentTarget;
const value = input.value;
if (value === '' || (/^\d*\.?\d*$/.test(value) &&
(value.includes('.')
? value.split('.')[1].length <= (transfer.baseToken?.representations[0]?.decimals ?? 0)
: true)
)) {
transfer.raw.updateField('amount', event);
} else {
input.value = transfer.raw.amount;
}
}}
class="text-center"
/>
112 changes: 112 additions & 0 deletions app2/src/lib/components/Transfer/Assets.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<script lang="ts">
import Label from "$lib/components/ui/Label.svelte";
import {getTransfer} from "../../../routes/transfer/transfer.svelte.ts";
import {Option} from "effect";
import {tokensStore} from "$lib/stores/tokens.svelte.ts";
import Input from "$lib/components/ui/Input.svelte";
import {fade, fly} from "svelte/transition";

const {transfer} = getTransfer();
let open = $state(false);
let searchQuery = $state("");

function ensureTokensForChain() {
const chainId = transfer.sourceChain?.universal_chain_id;
if (!chainId) return;
const tokenData = tokensStore.getData(chainId);
if (Option.isNone(tokenData)) {
tokensStore.fetchTokens(chainId);
}
}

$effect(() => {
if (transfer.sourceChain) {
ensureTokensForChain();
}
});

const filteredTokens = $derived.by(() => {
const query = searchQuery.toLowerCase();
return transfer.baseTokens.filter((token) => (
token.denom.toLowerCase().includes(query) ||
(token.representations[0]?.name?.toLowerCase() || "").includes(query)
));
});
</script>

{#if open}
<div
transition:fade="{{ duration: 500 }}"
class="absolute top-0 left-0 w-full h-full z-30 flex items-center justify-center bg-zinc-900"
>
<div
transition:fly="{{ y: 50, duration: 500, opacity: 0, delay: 100 }}"
class="w-full h-full rounded-lg p-4 shadow-lg flex flex-col"
>
<!-- Search Bar -->
<Input
label="Search"
type="text"
placeholder="Search by name or paste address"
value={searchQuery}
oninput={(e) => (searchQuery = (e.currentTarget as HTMLInputElement).value)}
/>

<!-- Scrollable Token List -->
{#if transfer.sourceChain}
{@const tokenData = tokensStore.getData(transfer.sourceChain.universal_chain_id)}
{@const error = tokensStore.getError(transfer.sourceChain.universal_chain_id)}

{#if Option.isSome(error)}
<p class="text-center text-red-500">Error: {error.value.message}</p>
{:else if Option.isNone(tokenData)}
<p class="text-center text-zinc-400">Loading tokens...</p>
{:else}
<ul class="space-y-2 overflow-y-auto flex-1">
{#each filteredTokens as token}
<li>
<button
class="w-full p-2 text-left text-zinc-200 hover:bg-[#2a3535] hover:text-white focus:outline-none focus:bg-[#2a3535] transition-all duration-200"
onclick={() => {
transfer.raw.updateField("asset", token.denom);
open = false;
searchQuery = "";
}}
>
<span class="font-medium">
{token.representations[0]?.name ?? token.denom}
</span>
{#if token.representations[0]?.name}
<span class="text-sm text-zinc-400 ml-2">({token.denom})</span>
{/if}
</button>
</li>
{/each}
</ul>
{/if}
{:else}
<p class="text-center text-zinc-400">No chain selected</p>
{/if}
</div>
</div>
{/if}
<div class="space-y-1">
<Label>Asset</Label>
<button
disabled={!transfer.sourceChain}
onclick={() => (open = !open)}
class="w-full p-2 rounded-lg border border-zinc-600 bg-zinc-700 text-zinc-200 hover:bg-zinc-600 hover:border-zinc-500 focus:outline-none focus:ring-2 focus:ring-sky-500 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 cursor-pointer"
>
{#if transfer.raw.asset && !transfer.baseToken}
<span class="flex items-center justify-center">
<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Loading...
</span>
{:else}
{(transfer.baseToken?.representations[0]?.name ?? transfer.raw.asset) || "Select asset"}
{/if}
</button>
</div>
80 changes: 80 additions & 0 deletions app2/src/lib/components/Transfer/Chain.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<script lang="ts">
import {Option} from "effect";
import {chains} from "$lib/stores/chains.svelte.ts";
import {cn} from "$lib/utils";
import {tokensStore} from "$lib/stores/tokens.svelte.ts";
import Label from "$lib/components/ui/Label.svelte";
import {getTransfer} from "../../../routes/transfer/transfer.svelte.ts";
import {fade, fly} from "svelte/transition";

type Props = {
type: "source" | "destination";
};

let {type}: Props = $props();
let open = $state(false);

const {transfer} = getTransfer();
</script>

{#if open}
<div class="absolute inset-0 dark:border-zinc-700 dark:bg-zinc-900 z-30 p-4" transition:fade="{{ duration: 500 }}">
<div class="flex flex-col items-start w-full gap-4"
transition:fly="{{ y: 30, duration: 500, delay: 100, opacity: 0 }}">
<h2>Select {type} chain</h2>
<div class="grid grid-cols-3 gap-2 w-full">
{#if Option.isSome(chains.data)}
{#each chains.data.value as chain}
<button
class="border border-white bg-white/15 rounded p-2 text-sm hover:bg-white/30 cursor-pointer"
onclick={() => {
transfer.raw.updateField(type, chain.chain_id);
tokensStore.fetchTokens(chain.universal_chain_id);
open = !open;
}}
>
{chain.display_name.split(" ")[0]}
</button>
{/each}
{:else}
Loading...
{/if}
</div>
</div>
</div>
{/if}
<div>
<Label>{type}</Label>
<button
onclick={() => (open = !open)}
class={cn(
"w-full p-2 rounded-lg border border-zinc-600 bg-zinc-700 text-zinc-200",
"hover:bg-zinc-600 hover:border-zinc-500",
"focus:outline-none focus:ring-2 focus:ring-sky-500",
"disabled:opacity-50 disabled:cursor-not-allowed",
"transition-all duration-200 cursor-pointer"
)}
>
{#if type === "source" && transfer.raw.source && !transfer.sourceChain}
<span class="flex items-center justify-center">
<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Loading...
</span>
{:else if type === "destination" && transfer.raw.destination && !transfer.destinationChain}
<span class="flex items-center justify-center">
<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Loading...
</span>
{:else if type === "source"}
{transfer.sourceChain ? transfer.sourceChain.display_name : `Select ${type} chain`}
{:else}
{transfer.destinationChain ? transfer.destinationChain.display_name : `Select ${type} chain`}
{/if}
</button>
</div>
22 changes: 22 additions & 0 deletions app2/src/lib/components/Transfer/Receiver.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script lang="ts">
import Input from "$lib/components/ui/Input.svelte";
import {getTransfer} from "../../../routes/transfer/transfer.svelte.ts";

const {transfer} = getTransfer()
</script>

<Input
type="text"
id="receiver"
label="receiver"
required={true}
disabled={!transfer.raw.destination}
autocorrect="off"
spellcheck="false"
autocomplete="off"
data-field="receiver"
placeholder="Enter destination address"
value={transfer.raw.receiver}
oninput={event => transfer.raw.updateField('receiver', event)}
class="text-center"
/>
Loading