Skip to content

Commit

Permalink
add signMessage, locales, update demo
Browse files Browse the repository at this point in the history
  • Loading branch information
AZbang committed Aug 8, 2023
1 parent a44dde3 commit df54ed7
Show file tree
Hide file tree
Showing 27 changed files with 839 additions and 163 deletions.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "near-snap",
"version": "0.4.1",
"version": "0.5.0",
"private": true,
"description": "",
"homepage": "https://github.com/here-wallet/near-snap#readme",
Expand All @@ -26,6 +26,9 @@
"start": "yarn workspaces foreach --parallel --interlaced --verbose run start",
"test": "echo \"TODO\""
},
"dependencies": {
"@near-wallet-selector/core": "^8.3.0"
},
"devDependencies": {
"@metamask/eslint-config": "^10.0.0",
"@metamask/eslint-config-jest": "^10.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@near-snap/sdk",
"version": "0.4.0",
"version": "0.5.0",
"homepage": "https://github.com/here-wallet/near-snap#readme",
"bugs": {
"url": "https://github.com/here-wallet/near-snap/issues"
Expand Down
47 changes: 45 additions & 2 deletions packages/sdk/src/account.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import crypto from 'crypto';
import {
DelegateAction,
SignedDelegate,
Expand All @@ -21,6 +22,7 @@ import { BN } from 'bn.js';
import { wait, waitTransactionResult } from './utils/waitTransaction';
import { convertAction } from './utils/convertAction';
import { createAction } from './utils/createAction';
import * as nep0413 from './utils/nep0413';

import { TransactionInListError, TransactionSignRejected } from './errors';
import { DelegatedTransaction, NearSnapStatus } from './types';
Expand Down Expand Up @@ -85,8 +87,49 @@ class NearSnapAccount extends Account {
});
}

async signMessage() {
// TODO
async authenticate(recipient: string, message: string) {
const nonce = crypto.randomBytes(32);
const request = { message, recipient, nonce, network: this.network };
const signed = await this.signMessage(request);

const isVerified = nep0413.verifySignature(request, signed);
if (!isVerified) {
throw Error('Signature is incorrect');
}

const keys = await this.getAccessKeys();
const isValid = keys.some((k) => {
return (
k.public_key === signed.publicKey &&
k.access_key.permission === 'FullAccess'
);
});

if (!isValid) {
throw Error('Signer public key is not full access');
}

return signed;
}

async signMessage(data: nep0413.SignMessageOptionsNEP0413) {
const signed = await this.snap.signMessage({
message: data.message,
nonce: Array.from(data.nonce),
recipient: data.recipient,
network: this.network,
});

if (!signed) {
throw Error('Signed result is undefined');
}

const { accountId, publicKey, signature } = signed;
if (!accountId || !publicKey || !signature) {
throw Error('Signed result is undefined');
}

return { accountId, publicKey, signature };
}

protected async signTransaction(
Expand Down
8 changes: 8 additions & 0 deletions packages/sdk/src/snap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {
Maybe,
NearSnapStatus,
SignDelegatedTransactionsParams,
SignMessageParams,
} from './types';
import { SignedMessageNEP0413 } from './utils/nep0413';

class NearSnap {
readonly id: string;
Expand Down Expand Up @@ -67,6 +69,12 @@ class NearSnap {
});
}

async signMessage(
data: SignMessageParams,
): Promise<Maybe<SignedMessageNEP0413>> {
return await this.provider.invokeSnap(this.id, 'near_signMessage', data);
}

async signDelegatedTransactions(
transaction: SignDelegatedTransactionsParams,
): Promise<Maybe<{ signature: string; transaction: string }>> {
Expand Down
7 changes: 7 additions & 0 deletions packages/sdk/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ export type SignTransactionsParams = {
network: NetworkId;
};

export type SignMessageParams = {
network: NetworkId;
message: string;
recipient: string;
nonce: number[];
};

export type SignDelegatedTransactionsParams = {
payer?: string;
network: NetworkId;
Expand Down
8 changes: 5 additions & 3 deletions packages/sdk/src/utils/convertAction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { transactions } from 'near-api-js';
import type { Action } from '@near-wallet-selector/core';

export const TGAS = Math.pow(10, 12);

// Decode binary to json or array
const decodeArgs = (args: Uint8Array) => {
try {
Expand Down Expand Up @@ -71,9 +73,9 @@ export const convertAction = (action: transactions.Action): Action => {
type: 'FunctionCall',
params: {
args: decodeArgs(action.functionCall.args),
deposit: action.functionCall.deposit.toString(),
gas: action.functionCall.gas.toString(),
methodName: action.functionCall.methodName,
deposit: action.functionCall.deposit?.toString() ?? '0',
gas: action.functionCall.gas?.toString() ?? String(300 * TGAS),
methodName: action.functionCall.methodName ?? '',
},
};
}
Expand Down
80 changes: 80 additions & 0 deletions packages/sdk/src/utils/nep0413.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import crypto from 'crypto';
import { PublicKey } from 'near-api-js/lib/utils';
import { serialize } from 'near-api-js/lib/utils/serialize';

export type SignMessageOptionsNEP0413 = {
message: string; // The message that wants to be transmitted.
recipient: string; // The recipient to whom the message is destined (e.g. "alice.near" or "myapp.com").
nonce: Buffer; // A nonce that uniquely identifies this instance of the message, denoted as a 32 bytes array (a fixed `Buffer` in JS/TS).
callbackUrl?: string; // Optional, applicable to browser wallets (e.g. MyNearWallet). The URL to call after the signing process. Defaults to `window.location.href`.
};

export type SignedMessageNEP0413 = {
signature: string;
publicKey: string;
accountId: string;
};

export class AuthPayload implements SignMessageOptionsNEP0413 {
readonly message: string;

readonly recipient: string;

readonly nonce: Buffer;

readonly callbackUrl?: string | undefined;

readonly tag: number;

constructor({
message,
nonce,
recipient,
callbackUrl,
}: SignMessageOptionsNEP0413) {
this.tag = 2147484061;
this.message = message;
this.nonce = nonce;
this.recipient = recipient;
if (callbackUrl) {
this.callbackUrl = callbackUrl;
}
}
}

export const authPayloadSchema = new Map([
[
AuthPayload,
{
kind: 'struct',
fields: [
['tag', 'u32'],
['message', 'string'],
['nonce', [32]],
['recipient', 'string'],
['callbackUrl', { kind: 'option', type: 'string' }],
],
},
],
]);

// eslint-disable-next-line jsdoc/require-jsdoc
export function verifySignature(
request: SignMessageOptionsNEP0413,
result: SignedMessageNEP0413,
) {
// Reconstruct the payload that was **actually signed**
const payload = new AuthPayload(request);
const borsh_payload = serialize(authPayloadSchema, payload);
const hash = crypto.createHash('sha256');
const to_sign = Uint8Array.from(hash.update(borsh_payload).digest());

// Reconstruct the signature from the parameter given in the URL
const real_signature = new Uint8Array(
Buffer.from(result.signature, 'base64'),
);

// Use the public Key to verify that the private-counterpart signed the message
const myPK = PublicKey.from(result.publicKey);
return myPK.verify(to_sign, real_signature);
}
2 changes: 1 addition & 1 deletion packages/site/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@near-snap/site",
"version": "0.2.0",
"version": "0.5.0",
"private": true,
"homepage": "https://github.com/here-wallet/near-snap#readme",
"bugs": {
Expand Down
Loading

0 comments on commit df54ed7

Please sign in to comment.