diff --git a/packages/my-near-wallet/src/lib/my-near-wallet-connection.ts b/packages/my-near-wallet/src/lib/my-near-wallet-connection.ts deleted file mode 100644 index bc6e969d2..000000000 --- a/packages/my-near-wallet/src/lib/my-near-wallet-connection.ts +++ /dev/null @@ -1,527 +0,0 @@ -import type { SignedMessage } from "@near-wallet-selector/core"; -import { serialize } from "borsh"; -import type { Connection, InMemorySigner, Near } from "near-api-js"; -import { Account, KeyPair } from "near-api-js"; -import type { SignAndSendTransactionOptions } from "near-api-js/lib/account"; -import type { KeyStore } from "near-api-js/lib/key_stores"; -import type { FinalExecutionOutcome } from "near-api-js/lib/providers"; -import type { AccessKeyInfoView } from "near-api-js/lib/providers/provider"; -import type { Action, Transaction } from "near-api-js/lib/transaction"; -import { SCHEMA, createTransaction } from "near-api-js/lib/transaction"; -import { PublicKey } from "near-api-js/lib/utils"; -import { base_decode } from "near-api-js/lib/utils/serialize"; - -const LOGIN_WALLET_URL_SUFFIX = "/login/"; -const MULTISIG_HAS_METHOD = "add_request_and_confirm"; -const LOCAL_STORAGE_KEY_SUFFIX = "_wallet_auth_key"; -const PENDING_ACCESS_KEY_PREFIX = "pending_key"; // browser storage key for a pending access key (i.e. key has been generated but we are not sure it was added yet) - -const DEFAULT_POPUP_WIDTH = 480; -const DEFAULT_POPUP_HEIGHT = 640; -const POLL_INTERVAL = 300; - -interface SignInOptions { - contractId?: string; - methodNames?: Array; - successUrl?: string; - failureUrl?: string; - keyType?: "ed25519" | "secp256k1"; -} - -interface WalletMessage { - status: "success" | "failure" | "pending"; - transactionHashes?: string; - error?: string; - [key: string]: unknown; - signedRequest?: SignedMessage; - errorMessage?: string; - errorCode?: string; -} - -/** - * Information to send NEAR wallet for signing transactions and redirecting the browser back to the calling application - */ -interface RequestSignTransactionsOptions { - /** list of transactions to sign */ - transactions: Array; - /** url NEAR Wallet will redirect to after transaction signing is complete */ - callbackUrl?: string; - /** meta information NEAR Wallet will send back to the application. `meta` will be attached to the `callbackUrl` as a url search param */ - meta?: string; -} - -interface AuthData { - accountId?: string; - allKeys?: Array; -} - -interface WalletResponseData extends WalletMessage { - public_key?: string; - all_keys?: Array; - account_id?: string; -} - -export class MyNearWalletConnection { - _walletBaseUrl: string; - _authDataKey: string; - _keyStore: KeyStore; - _authData: AuthData; - _networkId: string; - _near: Near; - _connectedAccount?: MyNearConnectedWalletAccount | null; - _completeSignInPromise?: Promise; - - constructor(near: Near, appKeyPrefix: string) { - if (typeof appKeyPrefix !== "string") { - throw new Error( - "Please define a clear appKeyPrefix for this WalletConnection instance" - ); - } - - this._near = near; - const authDataKey = appKeyPrefix + LOCAL_STORAGE_KEY_SUFFIX; - const authData = JSON.parse( - window.localStorage.getItem(authDataKey) || "{}" - ) as AuthData; - - this._networkId = near.config.networkId; - this._walletBaseUrl = near.config.walletUrl; - this._keyStore = (near.connection.signer as InMemorySigner).keyStore; - this._authData = authData; - this._authDataKey = authDataKey; - } - - isSignedIn(): boolean { - return !!this._authData.accountId; - } - - async isSignedInAsync(): Promise { - if (!this._completeSignInPromise) { - return this.isSignedIn(); - } - - await this._completeSignInPromise; - return this.isSignedIn(); - } - - getAccountId(): string { - return this._authData.accountId || ""; - } - - async requestSignInUrl({ - contractId, - methodNames, - successUrl, - failureUrl, - keyType = "ed25519", - }: SignInOptions): Promise { - const currentUrl = new URL(window.location.href); - const newUrl = new URL(this._walletBaseUrl + LOGIN_WALLET_URL_SUFFIX); - newUrl.searchParams.set("success_url", successUrl || currentUrl.href); - newUrl.searchParams.set("failure_url", failureUrl || currentUrl.href); - if (contractId) { - /* Throws exception if contract account does not exist */ - const contractAccount = await this._near.account(contractId); - await contractAccount.state(); - - newUrl.searchParams.set("contract_id", contractId); - const accessKey = KeyPair.fromRandom(keyType); - newUrl.searchParams.set( - "public_key", - accessKey.getPublicKey().toString() - ); - await this._keyStore.setKey( - this._networkId, - PENDING_ACCESS_KEY_PREFIX + accessKey.getPublicKey(), - accessKey - ); - } - - if (methodNames) { - methodNames.forEach((methodName) => { - newUrl.searchParams.append("methodNames", methodName); - }); - } - - return newUrl.toString(); - } - - async handlePopupTransaction( - url: string, - callback: (result: WalletMessage) => T - ): Promise { - const screenWidth = window.innerWidth || screen.width; - const screenHeight = window.innerHeight || screen.height; - const left = (screenWidth - DEFAULT_POPUP_WIDTH) / 2; - const top = (screenHeight - DEFAULT_POPUP_HEIGHT) / 2; - const childWindow = window.open( - url, - "My Near Wallet", - `width=${DEFAULT_POPUP_WIDTH},height=${DEFAULT_POPUP_HEIGHT},top=${top},left=${left}` - ); - - if (!childWindow) { - throw new Error( - "Popup window blocked. Please allow popups for this site." - ); - } - - return new Promise((resolve, reject) => { - const messageHandler = this.setupMessageHandler( - resolve, - reject, - childWindow, - callback - ); - - const intervalId = setInterval(() => { - if (childWindow.closed) { - window.removeEventListener("message", messageHandler); - clearInterval(intervalId); - reject(new Error("User closed the window")); - } - }, POLL_INTERVAL); - }); - } - - private setupMessageHandler( - resolve: (value: T) => void, - reject: (reason?: unknown) => void, - childWindow: Window | null, - callback: (result: WalletMessage) => T - ): (event: MessageEvent) => Promise { - const handler = async (event: MessageEvent) => { - const message = event.data as WalletMessage; - - // check if the URL are the same - const origin = new URL(event.origin); - const walletBaseUrl = new URL(this._walletBaseUrl); - if (origin.origin !== walletBaseUrl.origin) { - // eslint-disable-next-line no-console - console.warn("Ignoring message from different origin", origin.origin); - return; - } - - switch (message.status) { - case "success": - childWindow?.close(); - resolve(callback(message)); - break; - case "failure": - childWindow?.close(); - reject(new Error(message.errorMessage || "Transaction failed")); - break; - default: - // eslint-disable-next-line no-console - console.warn("Unhandled message status:", message.status); - } - }; - - window.addEventListener("message", handler); - return handler; - } - - async requestSignIn( - options: SignInOptions - ): Promise> { - const url = await this.requestSignInUrl(options); - return await this.handlePopupTransaction(url, async (data) => { - const responseData = data as WalletResponseData; - const { - public_key: publicKey, - all_keys: allKeys, - account_id: accountId, - } = responseData; - - if (accountId && publicKey && allKeys) { - await this.completeSignInWithAccessKeys({ - accountId, - publicKey, - allKeys, - }); - return [{ accountId, publicKey }]; - } - throw new Error("Invalid response data from wallet"); - }); - } - - requestSignTransactionsUrl(options: RequestSignTransactionsOptions): string { - const { transactions, meta, callbackUrl } = options; - const currentUrl = new URL(window.location.href); - const newUrl = new URL("sign", this._walletBaseUrl); - - newUrl.searchParams.set( - "transactions", - transactions - .map((transaction) => serialize(SCHEMA.Transaction, transaction)) - .map((serialized) => Buffer.from(serialized).toString("base64")) - .join(",") - ); - newUrl.searchParams.set("callbackUrl", callbackUrl || currentUrl.href); - - if (meta) { - newUrl.searchParams.set("meta", meta); - } - - return newUrl.toString(); - } - - async requestSignTransactions( - options: RequestSignTransactionsOptions - ): Promise> { - const url = this.requestSignTransactionsUrl(options); - const transactionsHashes = ( - await this.handlePopupTransaction(url, (data) => data.transactionHashes) - )?.split(","); - - if (!transactionsHashes) { - throw new Error("No transaction hashes received"); - } - - return Promise.all( - transactionsHashes.map((hash) => - this._near.connection.provider.txStatus(hash, "unused", "NONE") - ) - ); - } - - requestSignTransaction( - options: RequestSignTransactionsOptions - ): Promise { - const url = this.requestSignTransactionsUrl(options); - - return this.handlePopupTransaction( - url, - (data) => data?.transactionHashes - ) as Promise; - } - - async completeSignInWithAccessKeys({ - accountId, - allKeys, - publicKey, - }: { - accountId: string; - allKeys: Array; - publicKey: string; - }) { - const authData = { - accountId, - allKeys, - }; - window.localStorage.setItem(this._authDataKey, JSON.stringify(authData)); - if (publicKey) { - await this._moveKeyFromTempToPermanent(accountId, publicKey); - } - this._authData = authData; - this.updateAccount(); - } - - async _moveKeyFromTempToPermanent(accountId: string, publicKey: string) { - const keyPair = await this._keyStore.getKey( - this._networkId, - PENDING_ACCESS_KEY_PREFIX + publicKey - ); - await this._keyStore.setKey(this._networkId, accountId, keyPair); - await this._keyStore.removeKey( - this._networkId, - PENDING_ACCESS_KEY_PREFIX + publicKey - ); - } - - signOut() { - this._authData = {}; - window.localStorage.removeItem(this._authDataKey); - this._keyStore.clear(); - } - - /* eslint-disable @typescript-eslint/no-use-before-define */ - account() { - if (!this._connectedAccount) { - this._connectedAccount = new MyNearConnectedWalletAccount( - this, - this._near.connection, - this._authData.accountId || "" - ); - } - return this._connectedAccount; - } - - updateAccount() { - this._connectedAccount = new MyNearConnectedWalletAccount( - this, - this._near.connection, - this._authData.accountId || "" - ); - } - /* eslint-enable @typescript-eslint/no-use-before-define */ -} - -export class MyNearConnectedWalletAccount extends Account { - walletConnection: MyNearWalletConnection; - - constructor( - walletConnection: MyNearWalletConnection, - connection: Connection, - accountId: string - ) { - super(connection, accountId); - this.walletConnection = walletConnection; - } - - async signAndSendTransaction({ - receiverId, - actions, - walletMeta, - walletCallbackUrl = window.location.href, - }: SignAndSendTransactionOptions): Promise { - const localKey = await this.connection.signer.getPublicKey( - this.accountId, - this.connection.networkId - ); - let accessKey = await this.accessKeyForTransaction( - receiverId, - actions, - localKey - ); - if (!accessKey) { - throw new Error( - `Cannot find matching key for transaction sent to ${receiverId}` - ); - } - - if (localKey && localKey.toString() === accessKey.public_key) { - try { - return await super.signAndSendTransaction({ receiverId, actions }); - } catch (e: unknown) { - /* eslint-disable @typescript-eslint/no-use-before-define */ - if ( - typeof e === "object" && - e !== null && - "type" in e && - (e as any).type === "NotEnoughAllowance" // eslint-disable-line @typescript-eslint/no-explicit-any - ) { - accessKey = await this.accessKeyForTransaction(receiverId, actions); - } else { - throw e; - } - /* eslint-enable @typescript-eslint/no-use-before-define */ - } - } - - const block = await this.connection.provider.block({ finality: "final" }); - const blockHash = base_decode(block.header.hash); - - if (!accessKey) { - throw new Error("No matching key found for transaction"); - } - const publicKey = PublicKey.from(accessKey.public_key); - // TODO: Cache & listen for nonce updates for given access key - const nonce = accessKey.access_key.nonce + BigInt("1"); - const transaction = createTransaction( - this.accountId, - publicKey, - receiverId, - nonce, - actions, - blockHash - ); - const transactionHashes = - await this.walletConnection.requestSignTransaction({ - transactions: [transaction], - meta: walletMeta, - callbackUrl: walletCallbackUrl, - }); - - return new Promise((resolve, reject) => { - this.connection.provider - .txStatus(transactionHashes, "unused", "NONE") - .then(resolve) - .catch(reject); - - setTimeout(() => { - reject(new Error("Failed to redirect to sign transaction")); - }, 1000); - }); - - // TODO: Aggregate multiple transaction request with "debounce". - // TODO: Introduce TransactionQueue which also can be used to watch for status? - } - - async accessKeyMatchesTransaction( - accessKey: AccessKeyInfoView, - receiverId: string, - actions: Array - ): Promise { - const { - access_key: { permission }, - } = accessKey; - if (permission === "FullAccess") { - return true; - } - - if (permission.FunctionCall) { - const { receiver_id: allowedReceiverId, method_names: allowedMethods } = - permission.FunctionCall; - /******************************** - Accept multisig access keys and let wallets attempt to signAndSendTransaction - If an access key has itself as receiverId and method permission add_request_and_confirm, then it is being used in a wallet with multisig contract: https://github.com/near/core-contracts/blob/671c05f09abecabe7a7e58efe942550a35fc3292/multisig/src/lib.rs#L149-L153 - ********************************/ - if ( - allowedReceiverId === this.accountId && - allowedMethods.includes(MULTISIG_HAS_METHOD) - ) { - return true; - } - if (allowedReceiverId === receiverId) { - if (actions.length !== 1) { - return false; - } - const [{ functionCall }] = actions; - return !!( - functionCall && - (!functionCall.deposit || functionCall.deposit.toString() === "0") && // TODO: Should support charging amount smaller than allowance? - (allowedMethods.length === 0 || - allowedMethods.includes(functionCall.methodName)) - ); - // TODO: Handle cases when allowance doesn't have enough to pay for gas - } - } - // TODO: Support other permissions than FunctionCall - - return false; - } - - async accessKeyForTransaction( - receiverId: string, - actions: Array, - localKey?: PublicKey - ): Promise { - const accessKeys = await this.getAccessKeys(); - - if (localKey) { - const accessKey = accessKeys.find( - (key) => key.public_key.toString() === localKey.toString() - ); - if ( - accessKey && - (await this.accessKeyMatchesTransaction(accessKey, receiverId, actions)) - ) { - return accessKey; - } - } - - const walletKeys = this.walletConnection._authData.allKeys; - for (const accessKey of accessKeys) { - if ( - walletKeys && - walletKeys.indexOf(accessKey.public_key) !== -1 && - (await this.accessKeyMatchesTransaction(accessKey, receiverId, actions)) - ) { - return accessKey; - } - } - - return null; - } -} diff --git a/packages/my-near-wallet/src/lib/my-near-wallet.spec.ts b/packages/my-near-wallet/src/lib/my-near-wallet.spec.ts index 701f689cc..9c84817b6 100644 --- a/packages/my-near-wallet/src/lib/my-near-wallet.spec.ts +++ b/packages/my-near-wallet/src/lib/my-near-wallet.spec.ts @@ -1,26 +1,59 @@ /* eslint-disable @nx/enforce-module-boundaries */ +import type { + Near, + WalletConnection, + ConnectedWalletAccount, +} from "near-api-js"; +import type { AccountView } from "near-api-js/lib/providers/provider"; import { mock } from "jest-mock-extended"; -import { mockWallet } from "../../../core/src/lib/testUtils"; +import { mockWallet } from "../../../core/src/lib/testUtils"; import type { MockWalletDependencies } from "../../../core/src/lib/testUtils"; -import type { InjectedWallet } from "../../../core/src/lib/wallet"; -import { setupMyNearWallet } from "./my-near-wallet"; -import type { MyNearWalletConnection } from "./my-near-wallet-connection"; - -const accountId = "amirsaran.testnet"; -const publicKey = "GF7tLvSzcxX4EtrMFtGvGTb2yUj2DhL8hWzc97BwUkyC"; +import type { BrowserWallet } from "../../../core/src/lib/wallet"; const createMyNearWallet = async (deps: MockWalletDependencies = {}) => { - const walletConnection = mock(); + const walletConnection = mock(); + const account = mock({ + connection: { + signer: { + getPublicKey: jest.fn().mockReturnValue(""), + }, + }, + }); + + jest.mock("near-api-js", () => { + const module = jest.requireActual("near-api-js"); + return { + ...module, + connect: jest.fn().mockResolvedValue(mock()), + WalletConnection: jest.fn().mockReturnValue(walletConnection), + }; + }); - const { wallet } = await mockWallet( - setupMyNearWallet(), - deps + walletConnection.isSignedIn.calledWith().mockReturnValue(true); + walletConnection.getAccountId + .calledWith() + .mockReturnValue("test-account.testnet"); + walletConnection.account.calledWith().mockReturnValue(account); + // @ts-ignore + // near-api-js marks this method as protected. + // TODO: return value instead of null + account.signAndSendTransaction.calledWith().mockReturnValue(null); + account.state.calledWith().mockResolvedValue( + mock({ + amount: "1000000000000000000000000", + }) ); + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { setupMyNearWallet } = require("./my-near-wallet"); + const { wallet } = await mockWallet(setupMyNearWallet(), deps); + return { - walletConnection, + nearApiJs: require("near-api-js"), wallet, + walletConnection, + account, }; }; @@ -29,17 +62,17 @@ afterEach(() => { }); describe("signIn", () => { - it.skip("sign into mynearwallet", async () => { - const { wallet, walletConnection } = await createMyNearWallet(); + it("sign into near wallet", async () => { + const { wallet, nearApiJs } = await createMyNearWallet(); await wallet.signIn({ contractId: "test.testnet" }); - expect(walletConnection.requestSignIn).toHaveBeenCalled(); + expect(nearApiJs.connect).toHaveBeenCalled(); }); }); describe("signOut", () => { - it.skip("sign out of mynearwallet", async () => { + it("sign out of near wallet", async () => { const { wallet, walletConnection } = await createMyNearWallet(); await wallet.signIn({ contractId: "test.testnet" }); @@ -50,54 +83,83 @@ describe("signOut", () => { }); describe("getAccounts", () => { - it.skip("returns array of accounts", async () => { - const { wallet } = await createMyNearWallet(); + it("returns array of accounts", async () => { + const { wallet, walletConnection } = await createMyNearWallet(); await wallet.signIn({ contractId: "test.testnet" }); const result = await wallet.getAccounts(); - expect(result).toEqual([{ accountId, publicKey }]); + expect(walletConnection.getAccountId).toHaveBeenCalled(); + expect(result).toEqual([ + { accountId: "test-account.testnet", publicKey: "" }, + ]); }); }); describe("signAndSendTransaction", () => { - it.skip("sign transaction in mynearwallet", async () => { - const { wallet, walletConnection } = await createMyNearWallet(); + // TODO: Figure out why imports to core are returning undefined. + it("signs and sends transaction", async () => { + const { wallet, walletConnection, account } = await createMyNearWallet(); await wallet.signIn({ contractId: "test.testnet" }); - await wallet.signAndSendTransaction({ - signerId: accountId, - receiverId: "test.testnet", + const result = await wallet.signAndSendTransaction({ + receiverId: "guest-book.testnet", actions: [], }); expect(walletConnection.account).toHaveBeenCalled(); + // near-api-js marks this method as protected. + // @ts-ignore + expect(account.signAndSendTransaction).toHaveBeenCalled(); + // @ts-ignore + expect(account.signAndSendTransaction).toBeCalledWith({ + actions: [], + receiverId: "guest-book.testnet", + }); + expect(result).toEqual(null); }); }); -describe("signAndSendTransactions", () => { - it.skip("sign transactions in mynearwallet", async () => { - const { wallet, walletConnection } = await createMyNearWallet(); +describe("buildImportAccountsUrl", () => { + it("returns import url", async () => { + const { wallet } = await createMyNearWallet(); - const transactions = [ - { - signerId: accountId, - receiverId: "test.testnet", - actions: [], - }, - { - signerId: accountId, - receiverId: "test.testnet", - actions: [], - }, - ]; + expect(typeof wallet.buildImportAccountsUrl).toBe("function"); - await wallet.signIn({ contractId: "test.testnet" }); - const result = await wallet.signAndSendTransactions({ - transactions, + // @ts-ignore + expect(wallet?.buildImportAccountsUrl()).toEqual( + "https://testnet.mynearwallet.com/batch-import" + ); + }); +}); + +describe("signMessage", () => { + it("sign message", async () => { + const { wallet } = await createMyNearWallet(); + + const replace = window.location.replace; + + Object.defineProperty(window, "location", { + value: { replace: jest.fn() }, }); - expect(walletConnection.account).toHaveBeenCalled(); - expect(result.length).toEqual(transactions.length); + const attributes = { + message: "test message", + nonce: Buffer.from("30990309-30990309-390A303-292090"), + recipient: "test.app", + callbackUrl: "https://test.app", + }; + + const result = await wallet.signMessage!(attributes); + + const nonceBase64 = attributes.nonce.toString("base64"); + const urlParams = `https://testnet.mynearwallet.com/sign-message?message=test+message&nonce=${encodeURIComponent( + nonceBase64 + )}&recipient=test.app&callbackUrl=https%3A%2F%2Ftest.app`; + + expect(result).toBe(undefined); + expect(window.location.replace).toHaveBeenCalledWith(urlParams); + + window.location.replace = replace; }); }); diff --git a/packages/my-near-wallet/src/lib/my-near-wallet.ts b/packages/my-near-wallet/src/lib/my-near-wallet.ts index 0956222f3..5ac291a8d 100644 --- a/packages/my-near-wallet/src/lib/my-near-wallet.ts +++ b/packages/my-near-wallet/src/lib/my-near-wallet.ts @@ -2,15 +2,14 @@ import * as nearAPI from "near-api-js"; import type { WalletModuleFactory, WalletBehaviourFactory, + BrowserWallet, Transaction, Optional, Network, Account, - InjectedWallet, } from "@near-wallet-selector/core"; import { createAction } from "@near-wallet-selector/wallet-utils"; import icon from "./icon"; -import { MyNearWalletConnection } from "./my-near-wallet-connection"; export interface MyNearWalletParams { walletUrl?: string; @@ -21,7 +20,7 @@ export interface MyNearWalletParams { } interface MyNearWalletState { - wallet: MyNearWalletConnection; + wallet: nearAPI.WalletConnection; keyStore: nearAPI.keyStores.BrowserLocalStorageKeyStore; } @@ -57,7 +56,7 @@ const setupWalletState = async ( headers: {}, }); - const wallet = new MyNearWalletConnection(near, "near_app"); + const wallet = new nearAPI.WalletConnection(near, "near_app"); return { wallet, @@ -66,13 +65,14 @@ const setupWalletState = async ( }; const MyNearWallet: WalletBehaviourFactory< - InjectedWallet, + BrowserWallet, { params: MyNearWalletExtraOptions } > = async ({ metadata, options, store, params, logger, id }) => { const _state = await setupWalletState(params, options.network); const getAccounts = async (): Promise> => { const accountId = _state.wallet.getAccountId(); const account = _state.wallet.account(); + if (!accountId || !account) { return []; } @@ -131,7 +131,7 @@ const MyNearWallet: WalletBehaviourFactory< }; return { - async signIn({ contractId, methodNames }) { + async signIn({ contractId, methodNames, successUrl, failureUrl }) { const existingAccounts = await getAccounts(); if (existingAccounts.length) { @@ -141,6 +141,8 @@ const MyNearWallet: WalletBehaviourFactory< await _state.wallet.requestSignIn({ contractId, methodNames, + successUrl, + failureUrl, }); return getAccounts(); @@ -188,23 +190,22 @@ const MyNearWallet: WalletBehaviourFactory< href.searchParams.append("state", state); } - return await _state.wallet.handlePopupTransaction( - href.toString(), - (value) => { - return { - accountId: value?.signedRequest?.accountId || "", - publicKey: value?.signedRequest?.publicKey || "", - signature: value?.signedRequest?.signature || "", - }; - } - ); + window.location.replace(href.toString()); + + return; }, - async signAndSendTransaction({ signerId, receiverId, actions }) { + async signAndSendTransaction({ + signerId, + receiverId, + actions, + callbackUrl, + }) { logger.log("signAndSendTransaction", { signerId, receiverId, actions, + callbackUrl, }); const { contract } = store.getState(); @@ -212,16 +213,18 @@ const MyNearWallet: WalletBehaviourFactory< if (!_state.wallet.isSignedIn() || !contract) { throw new Error("Wallet not signed in"); } + const account = _state.wallet.account(); return account["signAndSendTransaction"]({ receiverId: receiverId || contract.contractId, actions: actions.map((action) => createAction(action)), + walletCallbackUrl: callbackUrl, }); }, - async signAndSendTransactions({ transactions }) { - logger.log("signAndSendTransactions", { transactions }); + async signAndSendTransactions({ transactions, callbackUrl }) { + logger.log("signAndSendTransactions", { transactions, callbackUrl }); if (!_state.wallet.isSignedIn()) { throw new Error("Wallet not signed in"); @@ -229,6 +232,7 @@ const MyNearWallet: WalletBehaviourFactory< return _state.wallet.requestSignTransactions({ transactions: await transformTransactions(transactions), + callbackUrl, }); }, @@ -242,11 +246,13 @@ export function setupMyNearWallet({ walletUrl, iconUrl = icon, deprecated = false, -}: MyNearWalletParams = {}): WalletModuleFactory { + successUrl = "", + failureUrl = "", +}: MyNearWalletParams = {}): WalletModuleFactory { return async (moduleOptions) => { return { id: "my-near-wallet", - type: "injected", + type: "browser", metadata: { name: "MyNearWallet", description: @@ -254,7 +260,9 @@ export function setupMyNearWallet({ iconUrl, deprecated, available: true, - downloadUrl: resolveWalletUrl(moduleOptions.options.network, walletUrl), + successUrl, + failureUrl, + walletUrl: resolveWalletUrl(moduleOptions.options.network, walletUrl), }, init: (options) => { return MyNearWallet({