Skip to content

Commit

Permalink
Merge branch 'leon/verify_scripts' of github.com:dfinity/oisy-wallet …
Browse files Browse the repository at this point in the history
…into leon/verify_scripts
  • Loading branch information
Sawchord committed Oct 3, 2024
2 parents 739511f + 645e0e1 commit 5ef0f0f
Show file tree
Hide file tree
Showing 21 changed files with 318 additions and 41 deletions.
9 changes: 9 additions & 0 deletions src/frontend/src/lib/api/backend.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
BtcSelectUserUtxosFeeParams,
GetUserProfileResponse
} from '$lib/types/api';
import type { AllowSigningResponse } from '$lib/types/backend';
import type { CanisterApiFunctionParams } from '$lib/types/canister';
import { Principal } from '@dfinity/principal';
import { assertNonNullish, isNullish, type QueryParams } from '@dfinity/utils';
Expand Down Expand Up @@ -134,6 +135,14 @@ export const selectUserUtxosFee = async ({
return btcSelectUserUtxosFee(params);
};

export const allowSigning = async ({
identity
}: CanisterApiFunctionParams): Promise<AllowSigningResponse> => {
const { allowSigning } = await backendCanister({ identity });

return allowSigning();
};

const backendCanister = async ({
identity,
nullishIdentityErrorMessage,
Expand Down
12 changes: 11 additions & 1 deletion src/frontend/src/lib/api/signer.api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { BitcoinNetwork, SignRequest } from '$declarations/signer/signer.did';
import type { BitcoinNetwork, SendBtcResponse, SignRequest } from '$declarations/signer/signer.did';
import { SignerCanister } from '$lib/canisters/signer.canister';
import { SIGNER_CANISTER_ID } from '$lib/constants/app.constants';
import type { BtcAddress, EthAddress } from '$lib/types/address';
import type { SendBtcParams } from '$lib/types/api';
import type { CanisterApiFunctionParams } from '$lib/types/canister';
import { Principal } from '@dfinity/principal';
import { assertNonNullish, isNullish } from '@dfinity/utils';
Expand Down Expand Up @@ -70,6 +71,15 @@ export const signPrehash = async ({
return signPrehash({ hash });
};

export const sendBtc = async ({
identity,
...params
}: CanisterApiFunctionParams<SendBtcParams>): Promise<SendBtcResponse> => {
const { sendBtc } = await signerCanister({ identity });

return sendBtc(params);
};

const signerCanister = async ({
identity,
nullishIdentityErrorMessage,
Expand Down
Binary file added src/frontend/src/lib/assets/preview-large.webp
Binary file not shown.
Binary file added src/frontend/src/lib/assets/preview-small.webp
Binary file not shown.
7 changes: 7 additions & 0 deletions src/frontend/src/lib/canisters/backend.canister.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
BtcSelectUserUtxosFeeParams,
GetUserProfileResponse
} from '$lib/types/api';
import type { AllowSigningResponse } from '$lib/types/backend';
import type { CreateCanisterOptions } from '$lib/types/canister';
import { Canister, createServices, toNullable, type QueryParams } from '@dfinity/utils';

Expand Down Expand Up @@ -161,4 +162,10 @@ export class BackendCanister extends Canister<BackendService> {

return response.Ok;
};

allowSigning = async (): Promise<AllowSigningResponse> => {
const { allow_signing } = this.caller({ certified: true });

return allow_signing();
};
}
34 changes: 32 additions & 2 deletions src/frontend/src/lib/canisters/signer.canister.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import type {
BitcoinNetwork,
_SERVICE as SignerService,
SignRequest
SendBtcResponse,
SignRequest,
_SERVICE as SignerService
} from '$declarations/signer/signer.did';
import { idlFactory as idlCertifiedFactorySigner } from '$declarations/signer/signer.factory.certified.did';
import { idlFactory as idlFactorySigner } from '$declarations/signer/signer.factory.did';
import { getAgent } from '$lib/actors/agents.ic';
import type { BtcAddress, EthAddress } from '$lib/types/address';
import type { SendBtcParams } from '$lib/types/api';
import type { CreateCanisterOptions } from '$lib/types/canister';
import { Canister, createServices } from '@dfinity/utils';
import { mapSignerCanisterBtcError } from './signer.errors';
Expand Down Expand Up @@ -86,4 +88,32 @@ export class SignerCanister extends Canister<SignerService> {

return sign_prehash(hash);
};

sendBtc = async ({
addressType,
feeSatoshis,
utxosToSpend,
...rest
}: SendBtcParams): Promise<SendBtcResponse> => {
const { btc_caller_send } = this.caller({
certified: true
});

const response = await btc_caller_send(
{
address_type: addressType,
utxos_to_spend: utxosToSpend,
fee_satoshis: feeSatoshis,
...rest
},
// TODO: Pass a payment type
[]
);

if ('Err' in response) {
throw mapSignerCanisterBtcError(response.Err);
}

return response.Ok;
};
}
5 changes: 3 additions & 2 deletions src/frontend/src/lib/canisters/signer.errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ export class SignerCanisterPaymentError extends Error {
}
}

export const mapSignerCanisterBtcError = (response: GetAddressError) => {
type SignerCanisterBtcError = GetAddressError;
export const mapSignerCanisterBtcError = (response: SignerCanisterBtcError) => {
if ('InternalError' in response) {
return new CanisterInternalError(response.InternalError.msg);
}
if ('PaymentError' in response) {
return new SignerCanisterPaymentError(response.PaymentError);
}
return new CanisterInternalError('Unknown GetAddressError');
return new CanisterInternalError('Unknown SignerCanisterBtcError');
};
13 changes: 13 additions & 0 deletions src/frontend/src/lib/components/auth/AuthGuard.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script lang="ts">
import { fade } from 'svelte/transition';
import LandingPage from '$lib/components/auth/LandingPage.svelte';
import { authNotSignedIn } from '$lib/derived/auth.derived';
</script>

{#if $authNotSignedIn}
<LandingPage />
{:else}
<div in:fade>
<slot />
</div>
{/if}
31 changes: 31 additions & 0 deletions src/frontend/src/lib/components/auth/LandingPage.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script lang="ts">
import previewLarge from '$lib/assets/preview-large.webp';
import previewSmall from '$lib/assets/preview-small.webp';
import HeroSignIn from '$lib/components/hero/HeroSignIn.svelte';
import Img from '$lib/components/ui/Img.svelte';
import { i18n } from '$lib/stores/i18n.store.js';
import { replaceOisyPlaceholders } from '$lib/utils/i18n.utils.js';
let ariaLabel: string;
$: ariaLabel = replaceOisyPlaceholders($i18n.auth.alt.preview);
</script>

<div
class="m-auto grid w-full max-w-screen-2.5xl grid-cols-1 flex-col items-center gap-12 overflow-hidden px-5 md:grid-cols-2 md:grid-rows-1 md:items-start md:gap-8 md:overflow-visible"
>
<div
class="flex w-full flex-1 flex-col items-center text-center md:items-start md:pl-8 md:pt-12 md:text-left"
>
<div class="md:absolute md:top-1/3">
<HeroSignIn />
</div>
</div>

<!-- TODO: determine if this value is specific/permanent or can be changed -->
<div class=" min-w-[1127px] flex-1 md:pt-12">
<picture aria-label={ariaLabel} class="w-full" role="presentation">
<source srcset={previewSmall} media="(max-width: 767px)" />
<Img src={previewLarge} alt={ariaLabel} />
</picture>
</div>
</div>
11 changes: 11 additions & 0 deletions src/frontend/src/lib/components/auth/SignIn.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<script lang="ts">
import IconLogin from '$lib/components/icons/IconLogin.svelte';
import ButtonIcon from '$lib/components/ui/ButtonIcon.svelte';
import { signIn } from '$lib/services/auth.services';
import { i18n } from '$lib/stores/i18n.store';
</script>

<ButtonIcon ariaLabel={$i18n.auth.text.authenticate} on:click={async () => await signIn({})}>
<IconLogin slot="icon" />
{$i18n.auth.text.authenticate}
</ButtonIcon>
3 changes: 3 additions & 0 deletions src/frontend/src/lib/components/hero/Header.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts">
import WalletConnect from '$eth/components/wallet-connect/WalletConnect.svelte';
import SignIn from '$lib/components/auth/SignIn.svelte';
import Alpha from '$lib/components/core/Alpha.svelte';
import Back from '$lib/components/core/Back.svelte';
import Menu from '$lib/components/core/Menu.svelte';
Expand Down Expand Up @@ -47,6 +48,8 @@
<Menu />
{:else}
<AboutMenu />

<SignIn />
{/if}
</div>
</header>
Expand Down
20 changes: 5 additions & 15 deletions src/frontend/src/lib/components/hero/Hero.svelte
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
<script lang="ts">
import Header from '$lib/components/hero/Header.svelte';
import HeroContent from '$lib/components/hero/HeroContent.svelte';
import HeroSignIn from '$lib/components/hero/HeroSignIn.svelte';
import { authNotSignedIn, authSignedIn } from '$lib/derived/auth.derived';
import { authSignedIn } from '$lib/derived/auth.derived';
export let usdTotal = false;
export let summary = false;
export let actions = true;
export let back: 'header' | 'hero' | undefined;
// We only want to display the "Sign-in" call to action on pages that actually are displaying any content in the Hero pane.
let heroContent = true;
$: heroContent = usdTotal || summary;
</script>

<div class="pt-6">
<Header back={back === 'header'} />

<article
class="main relative flex flex-col items-center rounded-lg pb-6 pt-10"
class:pb-16={$authNotSignedIn}
>
{#if $authSignedIn}
{#if $authSignedIn}
<article class="main relative flex flex-col items-center rounded-lg pb-6 pt-10">
<HeroContent {usdTotal} {summary} {actions} back={back === 'hero'} />
{:else if heroContent}
<HeroSignIn />
{/if}
</article>
</article>
{/if}
</div>
2 changes: 1 addition & 1 deletion src/frontend/src/lib/components/hero/HeroSignIn.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</script>

<div class="mb-7 mt-5 pt-2">
<h1 class="text-center text-4xl">
<h1 class="text-4xl">
{$i18n.auth.text.title_part_1}<br /><span class="text-primary"
>{$i18n.auth.text.title_part_2}</span
>
Expand Down
24 changes: 24 additions & 0 deletions src/frontend/src/lib/components/icons/IconLogin.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!-- source: ISC Lucide - please visit https://lucide.dev/license -->
<svg
width="22"
height="22"
viewBox="0 0 22 22"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M12.8333 2.75016C12.8333 2.2439 13.2437 1.8335 13.75 1.8335H17.4166C18.146 1.8335 18.8455 2.12323 19.3612 2.63895C19.8769 3.15468 20.1666 3.85415 20.1666 4.5835V17.4168C20.1666 18.1462 19.8769 18.8456 19.3612 19.3614C18.8455 19.8771 18.146 20.1668 17.4166 20.1668H13.75C13.2437 20.1668 12.8333 19.7564 12.8333 19.2502C12.8333 18.7439 13.2437 18.3335 13.75 18.3335H17.4166C17.6598 18.3335 17.8929 18.2369 18.0648 18.065C18.2367 17.8931 18.3333 17.6599 18.3333 17.4168V4.5835C18.3333 4.34038 18.2367 4.10722 18.0648 3.93531C17.8929 3.76341 17.6598 3.66683 17.4166 3.66683H13.75C13.2437 3.66683 12.8333 3.25642 12.8333 2.75016Z"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M8.51849 5.76849C8.87647 5.4105 9.45687 5.4105 9.81485 5.76849L14.3982 10.3518C14.7562 10.7098 14.7562 11.2902 14.3982 11.6482L9.81485 16.2315C9.45687 16.5895 8.87647 16.5895 8.51849 16.2315C8.1605 15.8735 8.1605 15.2931 8.51849 14.9352L12.4536 11L8.51849 7.06485C8.1605 6.70687 8.1605 6.12647 8.51849 5.76849Z"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M1.83331 11.0002C1.83331 10.4939 2.24372 10.0835 2.74998 10.0835H13.75C14.2562 10.0835 14.6666 10.4939 14.6666 11.0002C14.6666 11.5064 14.2562 11.9168 13.75 11.9168H2.74998C2.24372 11.9168 1.83331 11.5064 1.83331 11.0002Z"
/>
</svg>
3 changes: 2 additions & 1 deletion src/frontend/src/lib/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
"authenticate": "Open or Create"
},
"alt": {
"sign_in": "Sign in with Internet Identity to start using Oisy Wallet."
"sign_in": "Sign in with Internet Identity to start using Oisy Wallet.",
"preview": "Preview of $oisy_name once signed in, both on desktop and mobile"
},
"warning": {
"not_signed_in": "You are not signed in. Please sign in to continue.",
Expand Down
14 changes: 14 additions & 0 deletions src/frontend/src/lib/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import type {
UserProfile,
Utxo
} from '$declarations/backend/backend.did';
import type {
BitcoinAddressType,
BtcTxOutput,
BitcoinNetwork as SignerBitcoinNetwork,
Utxo as SignerUtxo
} from '$declarations/signer/signer.did';
import type { BtcAddress } from '$lib/types/address';
import { Principal } from '@dfinity/principal';

Expand Down Expand Up @@ -35,3 +41,11 @@ export interface BtcAddPendingTransactionParams extends BtcGetPendingTransaction
txId: Uint8Array | number[];
utxos: Utxo[];
}

export interface SendBtcParams {
feeSatoshis: [] | [bigint];
network: SignerBitcoinNetwork;
utxosToSpend: SignerUtxo[];
addressType: BitcoinAddressType;
outputs: BtcTxOutput[];
}
3 changes: 3 additions & 0 deletions src/frontend/src/lib/types/backend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { Result_1 } from '$declarations/backend/backend.did';

export type AllowSigningResponse = Result_1;
2 changes: 1 addition & 1 deletion src/frontend/src/lib/types/i18n.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ interface I18nNavigation {

interface I18nAuth {
text: { title_part_1: string; title_part_2: string; logout: string; authenticate: string };
alt: { sign_in: string };
alt: { sign_in: string; preview: string };
warning: { not_signed_in: string; session_expired: string };
error: {
no_internet_identity: string;
Expand Down
15 changes: 9 additions & 6 deletions src/frontend/src/routes/(app)/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts">
import { page } from '$app/stores';
import AuthGuard from '$lib/components/auth/AuthGuard.svelte';
import LoadersGuard from '$lib/components/core/LoadersGuard.svelte';
import Modals from '$lib/components/core/Modals.svelte';
import Hero from '$lib/components/hero/Hero.svelte';
Expand All @@ -24,10 +25,12 @@
back={route === 'settings' ? 'header' : route === 'transactions' ? 'hero' : undefined}
/>

<main class="pb-5 pt-8 sm:pb-12">
<LoadersGuard>
<slot />
</LoadersGuard>
</main>
<AuthGuard>
<main class="pb-5 pt-8 sm:pb-12">
<LoadersGuard>
<slot />
</LoadersGuard>
</main>

<Modals />
<Modals />
</AuthGuard>
31 changes: 31 additions & 0 deletions src/frontend/src/tests/lib/canisters/backend.canister.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -553,4 +553,35 @@ describe('backend.canister', () => {
await expect(res).rejects.toThrow(mockResponseError);
});
});

describe('allowSigning', () => {
it('should allow signing', async () => {
const response = { Ok: null };

service.allow_signing.mockResolvedValue(response);

const { allowSigning } = await createBackendCanister({
serviceOverride: service
});

const res = await allowSigning();

expect(service.allow_signing).toHaveBeenCalledTimes(1);
expect(res).toEqual(response);
});

it('should throw an error if allowSigning throws', async () => {
service.allow_signing.mockImplementation(async () => {
throw mockResponseError;
});

const { allowSigning } = await createBackendCanister({
serviceOverride: service
});

const res = allowSigning();

await expect(res).rejects.toThrow(mockResponseError);
});
});
});
Loading

0 comments on commit 5ef0f0f

Please sign in to comment.