Skip to content

Commit

Permalink
Merge pull request #391 from near/SQC-153/add-support-for-verify-owner
Browse files Browse the repository at this point in the history
feat: Add Support for verifyOwner
  • Loading branch information
kujtimprenkuSQA authored Aug 16, 2022
2 parents 4af327a + 91fdad0 commit cc04a95
Show file tree
Hide file tree
Showing 15 changed files with 235 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<div>
<button (click)="signOut()">Log out</button>
<button (click)="switchWallet()">Switch Wallet</button>
<button (click)="onVerifyOwner()">Verify Owner</button>
<button *ngIf="accounts.length > 1" (click)="switchAccount()">
Switch Account
</button>
Expand Down
17 changes: 17 additions & 0 deletions examples/angular/src/app/components/content/content.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,23 @@ export class ContentComponent implements OnInit, OnDestroy {
alert("Switched account to " + nextAccountId);
}

async onVerifyOwner() {
const wallet = await this.selector.wallet();
try {
const owner = await wallet.verifyOwner({
message: "test message for verification",
});

if (owner) {
alert(`Signature for verification: ${JSON.stringify(owner)}`);
}
} catch (err) {
const message =
err instanceof Error ? err.message : "Something went wrong";
alert(message);
}
}

subscribeToEvents() {
this.subscription = this.selector.store.observable
.pipe(
Expand Down
18 changes: 18 additions & 0 deletions examples/react/components/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,23 @@ const Content: React.FC = () => {
[selector, accountId]
);

const handleVerifyOwner = async () => {
const wallet = await selector.wallet();
try {
const owner = await wallet.verifyOwner({
message: "test message for verification",
});

if (owner) {
alert(`Signature for verification: ${JSON.stringify(owner)}`);
}
} catch (err) {
const message =
err instanceof Error ? err.message : "Something went wrong";
alert(message);
}
};

const handleSubmit = useCallback(
async (e: SubmitEvent) => {
e.preventDefault();
Expand Down Expand Up @@ -220,6 +237,7 @@ const Content: React.FC = () => {
<div>
<button onClick={handleSignOut}>Log out</button>
<button onClick={handleSwitchWallet}>Switch Wallet</button>
<button onClick={handleVerifyOwner}>Verify Owner</button>
{accounts.length > 1 && (
<button onClick={handleSwitchAccount}>Switch Account</button>
)}
Expand Down
29 changes: 29 additions & 0 deletions packages/core/docs/api/wallet.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,35 @@ Returns one or more accounts when signed in. This method can be useful for walle
})();
```

### `.verifyOwner(params)`

**Parameters**
- `params` (`object`)
- `message` (`string`): The message requested sign. Defaults to `verify owner` string.
- `callbackUrl` (`string?`): Applicable to browser wallets (e.g. MyNearWallet). This is the callback url once the signing is approved. Defaults to `window.location.href`.
- `meta` (`string?`): Applicable to browser wallets (e.g. MyNearWallet) extra data that will be passed to the callback url once the signing is approved.

**Returns**
- `Promise<void | VerifiedOwner>`: Browser wallets won't return the signing outcome as they may need to redirect for signing. For MyNearWallet the outcome is passed to the callback url.

**Description**

Signs the message and verifies the owner. Message is not sent to blockchain.

> Note: This feature is currently supported only by MyNearWallet on **testnet**. Sender can sign messages when unlocked.
**Example**

```ts
// MyNearWallet
(async () => {
const wallet = await selector.wallet("my-near-wallet");
await wallet.verifyOwner({
message: "Test message",
});
})();
```


### `.signAndSendTransaction(params)`

**Parameters**
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export type {
BridgeWalletMetadata,
BridgeWalletBehaviour,
BridgeWallet,
VerifiedOwner,
VerifyOwnerParams,
Account,
Transaction,
Action,
Expand Down
18 changes: 17 additions & 1 deletion packages/core/src/lib/wallet/wallet.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { providers } from "near-api-js";
import { providers, utils } from "near-api-js";

import {
EventEmitterService,
Expand Down Expand Up @@ -29,6 +29,21 @@ export interface SignInParams {
methodNames?: Array<string>;
}

export interface VerifyOwnerParams {
message: string;
callbackUrl?: string;
meta?: string;
}

export interface VerifiedOwner {
accountId: string;
message: string;
blockId: string;
publicKey: string;
signature: string;
keyType: utils.key_pair.KeyType;
}

export interface SignAndSendTransactionParams {
signerId?: string;
receiverId?: string;
Expand All @@ -43,6 +58,7 @@ interface BaseWalletBehaviour {
signIn(params: SignInParams): Promise<Array<Account>>;
signOut(): Promise<void>;
getAccounts(): Promise<Array<Account>>;
verifyOwner(params: VerifyOwnerParams): Promise<VerifiedOwner | void>;
signAndSendTransaction(
params: SignAndSendTransactionParams
): Promise<providers.FinalExecutionOutcome>;
Expand Down
7 changes: 7 additions & 0 deletions packages/ledger/src/lib/ledger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const Ledger: WalletBehaviourFactory<HardwareWallet> = async ({
provider,
logger,
storage,
metadata,
}) => {
const _state = await setupLedgerState(storage);

Expand Down Expand Up @@ -219,6 +220,12 @@ const Ledger: WalletBehaviourFactory<HardwareWallet> = async ({
return getAccounts();
},

async verifyOwner({ message }) {
logger.log("Ledger:verifyOwner", { message });

throw new Error(`Method not supported by ${metadata.name}`);
},

async signAndSendTransaction({ signerId, receiverId, actions }) {
logger.log("signAndSendTransaction", { signerId, receiverId, actions });

Expand Down
39 changes: 39 additions & 0 deletions packages/math-wallet/src/lib/math-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const setupMathWalletState = (): MathWalletState => {
};

const MathWallet: WalletBehaviourFactory<InjectedWallet> = async ({
metadata,
options,
store,
provider,
Expand Down Expand Up @@ -104,6 +105,44 @@ const MathWallet: WalletBehaviourFactory<InjectedWallet> = async ({
return getAccounts();
},

async verifyOwner({ message }) {
logger.log("MathWallet:verifyOwner", { message });

const account = getActiveAccount(store.getState());

if (!account) {
throw new Error("No active account");
}

const accountId = account.accountId;
const pubKey = await _state.wallet.signer.getPublicKey(accountId);
const block = await provider.block({ finality: "final" });

const data = {
accountId,
message,
blockId: block.header.hash,
publicKey: Buffer.from(pubKey.data).toString("base64"),
keyType: pubKey.keyType,
};
const encoded = JSON.stringify(data);

// Note: Math Wallet currently hangs when calling signMessage.
throw new Error(`Method not supported by ${metadata.name}`);

const signed = await _state.wallet.signer.signMessage(
new Uint8Array(Buffer.from(encoded)),
accountId,
options.network.networkId
);

return {
...data,
signature: Buffer.from(signed.signature).toString("base64"),
keyType: signed.publicKey.keyType,
};
},

async signAndSendTransaction({ signerId, receiverId, actions }) {
logger.log("signAndSendTransaction", { signerId, receiverId, actions });
const signedTransactions = await signTransactions(
Expand Down
8 changes: 7 additions & 1 deletion packages/meteor-wallet/src/lib/meteor-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const setupWalletState = async (
const createMeteorWalletInjected: WalletBehaviourFactory<
InjectedWallet,
{ params: MeteorWalletParams_Injected }
> = async ({ options, logger, store, params }) => {
> = async ({ metadata, options, logger, store, params }) => {
const _state = await setupWalletState(params, options.network);

const cleanup = () => {
Expand Down Expand Up @@ -155,6 +155,12 @@ const createMeteorWalletInjected: WalletBehaviourFactory<
return getAccounts();
},

async verifyOwner({ message }) {
logger.log("MeteorWallet:verifyOwner", { message });

throw new Error(`Method not supported by ${metadata.name}`);
},

async signAndSendTransaction({ signerId, receiverId, actions }) {
logger.log("MeteorWallet:signAndSendTransaction", {
signerId,
Expand Down
29 changes: 28 additions & 1 deletion packages/my-near-wallet/src/lib/my-near-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const setupWalletState = async (
const MyNearWallet: WalletBehaviourFactory<
BrowserWallet,
{ params: MyNearWalletExtraOptions }
> = async ({ options, store, params, logger }) => {
> = async ({ metadata, options, store, params, logger }) => {
const _state = await setupWalletState(params, options.network);

const cleanup = () => {
Expand Down Expand Up @@ -155,6 +155,33 @@ const MyNearWallet: WalletBehaviourFactory<
return getAccounts();
},

async verifyOwner({ message, callbackUrl, meta }) {
logger.log("verifyOwner", { message });

const account = _state.wallet.account();

if (!account) {
throw new Error("Wallet not signed in");
}
const locationUrl =
typeof window !== "undefined" ? window.location.href : "";

const url = callbackUrl || locationUrl;

if (!url) {
throw new Error(`The callbackUrl is missing for ${metadata.name}`);
}

const encodedUrl = encodeURIComponent(url);
const extraMeta = meta ? `&meta=${meta}` : "";

window.location.replace(
`${params.walletUrl}/verify-owner?message=${message}&callbackUrl=${encodedUrl}${extraMeta}`
);

return;
},

async signAndSendTransaction({
signerId,
receiverId,
Expand Down
8 changes: 7 additions & 1 deletion packages/nightly-connect/src/lib/nightly-connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const setupNightlyConnectState = (): NightlyConnectState => {
const NightlyConnect: WalletBehaviourFactory<
BridgeWallet,
{ params: NightlyConnectParams }
> = async ({ store, params, logger, options, provider, emitter }) => {
> = async ({ metadata, store, params, logger, options, provider, emitter }) => {
const _state = setupNightlyConnectState();

const getAccounts = () => {
Expand Down Expand Up @@ -156,6 +156,12 @@ const NightlyConnect: WalletBehaviourFactory<
return getAccounts().map(({ accountId }) => ({ accountId }));
},

async verifyOwner({ message }) {
logger.log("NightlyConnect:verifyOwner", { message });

throw new Error(`Method not supported by ${metadata.name}`);
},

async signAndSendTransaction({ signerId, receiverId, actions }) {
logger.log("signAndSendTransaction", { signerId, receiverId, actions });

Expand Down
7 changes: 7 additions & 0 deletions packages/nightly/src/lib/nightly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const isInstalled = () => {
return waitFor(() => !!window.nightly!.near!).catch(() => false);
};
const Nightly: WalletBehaviourFactory<InjectedWallet> = async ({
metadata,
options,
store,
logger,
Expand Down Expand Up @@ -141,6 +142,12 @@ const Nightly: WalletBehaviourFactory<InjectedWallet> = async ({
return getAccounts().map(({ accountId }) => ({ accountId }));
},

async verifyOwner({ message }) {
logger.log("Nightly:verifyOwner", { message });

throw new Error(`Method not supported by ${metadata.name}`);
},

async signAndSendTransaction({ signerId, receiverId, actions }) {
logger.log("signAndSendTransaction", { signerId, receiverId, actions });

Expand Down
3 changes: 2 additions & 1 deletion packages/sender/src/lib/injected-sender.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Interfaces based on "documentation": https://github.com/SenderWallet/sender-wallet-integration-tutorial

// Empty string if we haven't signed in before.
import { providers } from "near-api-js";
import { Account, providers } from "near-api-js";

interface AccessKey {
publicKey: {
Expand Down Expand Up @@ -124,6 +124,7 @@ export interface InjectedSender {
callbacks: Record<keyof SenderEvents, unknown>;
getAccountId: () => string | null;
getRpc: () => Promise<GetRpcResponse>;
account(): Account | null;
requestSignIn: (
params: RequestSignInParams
) => Promise<RequestSignInResponse>;
Expand Down
Loading

0 comments on commit cc04a95

Please sign in to comment.