diff --git a/.changeset/poor-apricots-beg.md b/.changeset/poor-apricots-beg.md new file mode 100644 index 00000000..8dcf53b2 --- /dev/null +++ b/.changeset/poor-apricots-beg.md @@ -0,0 +1,7 @@ +--- +"@aptos-labs/wallet-adapter-react": minor +"@aptos-labs/wallet-adapter-core": minor +"@aptos-labs/wallet-adapter-nextjs-example": patch +--- + +Add support for a dapp generated api key diff --git a/apps/nextjs-example/.gitignore b/apps/nextjs-example/.gitignore index fd3dbb57..bcf93d7c 100644 --- a/apps/nextjs-example/.gitignore +++ b/apps/nextjs-example/.gitignore @@ -1,5 +1,7 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +.env + # dependencies /node_modules /.pnp diff --git a/apps/nextjs-example/src/components/WalletProvider.tsx b/apps/nextjs-example/src/components/WalletProvider.tsx index c72862a1..f195c3cf 100644 --- a/apps/nextjs-example/src/components/WalletProvider.tsx +++ b/apps/nextjs-example/src/components/WalletProvider.tsx @@ -33,7 +33,8 @@ export const WalletProvider = ({ children }: PropsWithChildren) => { autoConnect={autoConnect} dappConfig={{ network: Network.TESTNET, - aptosConnectDappId: "57fa42a9-29c6-4f1e-939c-4eefa36d9ff5", + aptosApiKey: process.env.NEXT_PUBLIC_APTOS_API_KEY, + aptosConnect: { dappId: "57fa42a9-29c6-4f1e-939c-4eefa36d9ff5" }, mizuwallet: { manifestURL: "https://assets.mz.xyz/static/config/mizuwallet-connect-manifest.json", diff --git a/packages/wallet-adapter-core/src/LegacyWalletPlugins/WalletCoreV1.ts b/packages/wallet-adapter-core/src/LegacyWalletPlugins/WalletCoreV1.ts index 905aae49..3b3de037 100644 --- a/packages/wallet-adapter-core/src/LegacyWalletPlugins/WalletCoreV1.ts +++ b/packages/wallet-adapter-core/src/LegacyWalletPlugins/WalletCoreV1.ts @@ -33,6 +33,7 @@ import { generalizedErrorMessage, getAptosConfig, } from "../utils"; +import { DappConfig } from "../WalletCore"; export class WalletCoreV1 extends EventEmitter { async connect(wallet: Wallet) { @@ -54,11 +55,12 @@ export class WalletCoreV1 extends EventEmitter { payloadData: InputGenerateTransactionPayloadData, network: NetworkInfo | null, wallet: Wallet, - transactionInput: InputTransactionData + transactionInput: InputTransactionData, + dappConfig?: DappConfig ) { // first check if each argument is a BCS serialized argument if (areBCSArguments(payloadData.functionArguments)) { - const aptosConfig = getAptosConfig(network); + const aptosConfig = getAptosConfig(network, dappConfig); const newPayload = await generateTransactionPayload({ ...(payloadData as InputEntryFunctionDataWithRemoteABI), aptosConfig: aptosConfig, diff --git a/packages/wallet-adapter-core/src/WalletCore.ts b/packages/wallet-adapter-core/src/WalletCore.ts index da993038..fdd87aa6 100644 --- a/packages/wallet-adapter-core/src/WalletCore.ts +++ b/packages/wallet-adapter-core/src/WalletCore.ts @@ -86,12 +86,20 @@ import type { AptosConnectWalletConfig } from "@aptos-connect/wallet-adapter-plu export type IAptosWallet = AptosStandardWallet & Wallet; +/** + * Interface for dapp configuration + * + * @network The network the dapp is working with + * @aptosApiKey An Api Key generated with {@link https://developers.aptoslabs.com/docs/api-access} + * @aptosConnect Config used to initialize the AptosConnect wallet provider + * @mizuwallet Config used to initialize the Mizu wallet provider + */ export interface DappConfig { network: Network; + aptosApiKey?: string; /** @deprecated */ aptosConnectDappId?: string; - // Config used to initialize the AptosConnect wallet provider - aptosConnect?: Omit, + aptosConnect?: Omit; mizuwallet?: { manifestURL: string; appId?: string; @@ -162,7 +170,7 @@ export class WalletCore extends EventEmitter { plugins: ReadonlyArray, optInWallets: ReadonlyArray, dappConfig?: DappConfig, - disableTelemetry?: boolean, + disableTelemetry?: boolean ) { super(); @@ -195,7 +203,7 @@ export class WalletCore extends EventEmitter { // new standard version installed. Pontem uses "Pontem" wallet name for previous versions and // "Pontem Wallet" with new version const existingStandardPontemWallet = this._standard_wallets.find( - (wallet) => wallet.name == "Pontem Wallet", + (wallet) => wallet.name == "Pontem Wallet" ); if (wallet.name === "Pontem" && existingStandardPontemWallet) { return; @@ -207,7 +215,7 @@ export class WalletCore extends EventEmitter { * include the plugin wallet (i.e npm package) */ const existingWalletIndex = this._standard_wallets.findIndex( - (standardWallet) => standardWallet.name == wallet.name, + (standardWallet) => standardWallet.name == wallet.name ); if (existingWalletIndex !== -1) return; @@ -262,7 +270,7 @@ export class WalletCore extends EventEmitter { aptosStandardSupportedWalletList.map((supportedWallet) => { // Check if we already have this wallet as an installed plugin const existingPluginWalletIndex = this.wallets.findIndex( - (wallet) => wallet.name === supportedWallet.name, + (wallet) => wallet.name === supportedWallet.name ); // If the plugin wallet is installed, dont append and dont show it on the selector modal @@ -273,7 +281,7 @@ export class WalletCore extends EventEmitter { // new standard version installed. Pontem uses "Pontem" wallet name for previous versions and // "Pontem Wallet" with new version const existingStandardPontemWallet = this.wallets.find( - (wallet) => wallet.name == "Pontem", + (wallet) => wallet.name == "Pontem" ); if ( supportedWallet.name === "Pontem Wallet" && @@ -284,7 +292,7 @@ export class WalletCore extends EventEmitter { // Check if we already have this wallet as a AIP-62 wallet standard const existingStandardWallet = this._standard_wallets.find( - (wallet) => wallet.name == supportedWallet.name, + (wallet) => wallet.name == supportedWallet.name ); // If AIP-62 wallet detected but it is excluded by the dapp, dont add it to the wallets array @@ -322,7 +330,7 @@ export class WalletCore extends EventEmitter { // Twallet SDK fires a register event so the adapter assumes it is an extension wallet // so filter out t wallet, remove it when twallet fixes it const wallets = extensionwWallets.filter( - (wallet) => wallet.name !== "Dev T wallet" && wallet.name !== "T wallet", + (wallet) => wallet.name !== "Dev T wallet" && wallet.name !== "T wallet" ); wallets.map((wallet: AptosStandardWallet) => { @@ -380,7 +388,7 @@ export class WalletCore extends EventEmitter { * @param standardWallet An AIP-62 standard compatible wallet */ private standardizeStandardWalletToPluginWalletType = ( - standardWallet: AptosStandardWallet, + standardWallet: AptosStandardWallet ) => { let standardWalletConvertedToWallet: Wallet = { name: standardWallet.name as WalletName, @@ -392,27 +400,28 @@ export class WalletCore extends EventEmitter { network: standardWallet.features["aptos:network"].network, account: standardWallet.features["aptos:account"].account, signAndSubmitTransaction: - standardWallet.features["aptos:signAndSubmitTransaction"] - ?.signAndSubmitTransaction, + standardWallet.features["aptos:signAndSubmitTransaction"] + ?.signAndSubmitTransaction, signMessage: standardWallet.features["aptos:signMessage"].signMessage, onAccountChange: - standardWallet.features["aptos:onAccountChange"].onAccountChange, + standardWallet.features["aptos:onAccountChange"].onAccountChange, onNetworkChange: - standardWallet.features["aptos:onNetworkChange"].onNetworkChange, + standardWallet.features["aptos:onNetworkChange"].onNetworkChange, signTransaction: - standardWallet.features["aptos:signTransaction"].signTransaction, + standardWallet.features["aptos:signTransaction"].signTransaction, openInMobileApp: - standardWallet.features["aptos:openInMobileApp"]?.openInMobileApp, + standardWallet.features["aptos:openInMobileApp"]?.openInMobileApp, changeNetwork: - standardWallet.features["aptos:changeNetwork"]?.changeNetwork, + standardWallet.features["aptos:changeNetwork"]?.changeNetwork, readyState: WalletReadyState.Installed, isAIP62Standard: true, - isSignTransactionV1_1: standardWallet.features["aptos:signTransaction"]?.version === '1.1', + isSignTransactionV1_1: + standardWallet.features["aptos:signTransaction"]?.version === "1.1", }; // Remove optional duplications in the _all_wallets array this._all_wallets = this._all_wallets.filter( - (item) => item.name !== standardWalletConvertedToWallet.name, + (item) => item.name !== standardWalletConvertedToWallet.name ); this._all_wallets.push(standardWalletConvertedToWallet); @@ -454,7 +463,7 @@ export class WalletCore extends EventEmitter { * @param account An account */ private ensureAccountExists( - account: AccountInfo | null, + account: AccountInfo | null ): asserts account is AccountInfo { if (!account) { throw new WalletAccountError("Account is not set").name; @@ -507,9 +516,7 @@ export class WalletCore extends EventEmitter { return; } - const aptosConfig = new AptosConfig({ - network: convertNetwork(this._network), - }); + const aptosConfig = getAptosConfig(this._network, this._dappConfig); const aptos = new Aptos(aptosConfig); const name = await aptos.ans.getPrimaryName({ address: this._account.address.toString(), @@ -542,7 +549,7 @@ export class WalletCore extends EventEmitter { | AccountInfo | StandardAccountInfo | UserResponse - | null, + | null ): void { if (account === null) { this._account = null; @@ -711,7 +718,7 @@ export class WalletCore extends EventEmitter { const allDetectedWallets = this._all_wallets as Array; const selectedWallet = allDetectedWallets.find( - (wallet: Wallet) => wallet.name === walletName, + (wallet: Wallet) => wallet.name === walletName ); if (!selectedWallet) return; @@ -720,7 +727,7 @@ export class WalletCore extends EventEmitter { // if the selected wallet is already connected, we don't need to connect again if (this._wallet?.name === walletName) throw new WalletConnectionError( - `${walletName} wallet is already connected`, + `${walletName} wallet is already connected` ).message; } @@ -819,7 +826,7 @@ export class WalletCore extends EventEmitter { * @returns The pending transaction hash (V1 output) | PendingTransactionResponse (V2 output) */ async signAndSubmitTransaction( - transactionInput: InputTransactionData, + transactionInput: InputTransactionData ): Promise< { hash: Types.HexEncodedBytes; output?: any } | PendingTransactionResponse > { @@ -839,7 +846,7 @@ export class WalletCore extends EventEmitter { this.recordEvent("sign_and_submit_transaction"); // get the payload piece from the input const payloadData = transactionInput.data; - const aptosConfig = getAptosConfig(this._network); + const aptosConfig = getAptosConfig(this._network, this._dappConfig); const aptos = new Aptos(aptosConfig); @@ -852,7 +859,7 @@ export class WalletCore extends EventEmitter { aptos, this._account, this._wallet, - this._standard_wallets, + this._standard_wallets ); return { hash, output }; } else { @@ -863,6 +870,7 @@ export class WalletCore extends EventEmitter { this._network, this._wallet, transactionInput, + this._dappConfig ); return { hash, output }; } @@ -903,7 +911,7 @@ export class WalletCore extends EventEmitter { async signTransaction( transactionOrPayload: AnyRawTransaction | Types.TransactionPayload, asFeePayer?: boolean, - options?: InputGenerateTransactionOptions, + options?: InputGenerateTransactionOptions ): Promise { try { this.ensureWalletExists(this._wallet); @@ -920,32 +928,41 @@ export class WalletCore extends EventEmitter { return await this.walletStandardCore.signTransaction( transactionOrPayload, this._wallet, - asFeePayer, + asFeePayer ); } else if (this._wallet.isSignTransactionV1_1) { // This wallet is AIP-62 compliant and supports transaction inputs const payload = convertPayloadInputV1ToV2(transactionOrPayload); - const optionsV1 = options as CompatibleTransactionOptions | undefined; - const { authenticator } = await this.walletStandardCore.signTransaction( - { - payload, - expirationTimestamp: optionsV1?.expireTimestamp ?? optionsV1?.expirationTimestamp, - expirationSecondsFromNow: optionsV1?.expirationSecondsFromNow, - gasUnitPrice: optionsV1?.gasUnitPrice ?? optionsV1?.gas_unit_price, - maxGasAmount: optionsV1?.maxGasAmount ?? optionsV1?.max_gas_amount, - sequenceNumber: optionsV1?.sequenceNumber, - sender: optionsV1?.sender ? { address: AccountAddress.from(optionsV1.sender) } : undefined, - }, - this._wallet, - ); + const optionsV1 = options as + | CompatibleTransactionOptions + | undefined; + const { authenticator } = + await this.walletStandardCore.signTransaction( + { + payload, + expirationTimestamp: + optionsV1?.expireTimestamp ?? + optionsV1?.expirationTimestamp, + expirationSecondsFromNow: optionsV1?.expirationSecondsFromNow, + gasUnitPrice: + optionsV1?.gasUnitPrice ?? optionsV1?.gas_unit_price, + maxGasAmount: + optionsV1?.maxGasAmount ?? optionsV1?.max_gas_amount, + sequenceNumber: optionsV1?.sequenceNumber, + sender: optionsV1?.sender + ? { address: AccountAddress.from(optionsV1.sender) } + : undefined, + }, + this._wallet + ); return authenticator; } else { - const aptosConfig = getAptosConfig(this._network); + const aptosConfig = getAptosConfig(this._network, this._dappConfig); this.ensureAccountExists(this._account); const sender = this._account.address; const payload = await generateTransactionPayloadFromV1Input( aptosConfig, - transactionOrPayload, + transactionOrPayload ); const optionsV1 = options as CompatibleTransactionOptions; const optionsV2 = { @@ -966,7 +983,7 @@ export class WalletCore extends EventEmitter { return await this.walletStandardCore.signTransaction( new SimpleTransaction(rawTransaction), this._wallet, - false, + false ); } } @@ -977,7 +994,7 @@ export class WalletCore extends EventEmitter { if ("rawTransaction" in transactionOrPayload) { const accountAuthenticator = (await this._wallet.signTransaction( transactionOrPayload, - asFeePayer, + asFeePayer )) as AccountAuthenticator; return accountAuthenticator; @@ -992,7 +1009,7 @@ export class WalletCore extends EventEmitter { gas_unit_price: options?.gasUnitPrice ? BigInt(options?.gasUnitPrice) : undefined, - }, + } ); if (!response) { @@ -1011,7 +1028,7 @@ export class WalletCore extends EventEmitter { const accountAuthenticator = new AccountAuthenticatorEd25519( new Ed25519PublicKey(publicKey), - new Ed25519Signature(signature), + new Ed25519Signature(signature) ); return accountAuthenticator; } @@ -1019,7 +1036,7 @@ export class WalletCore extends EventEmitter { // If we are here it means this wallet does not support signTransaction throw new WalletNotSupportedMethod( - `Sign Transaction is not supported by ${this.wallet?.name}`, + `Sign Transaction is not supported by ${this.wallet?.name}` ).message; } catch (error: any) { const errMsg = generalizedErrorMessage(error); @@ -1056,7 +1073,7 @@ export class WalletCore extends EventEmitter { * @returns PendingTransactionResponse */ async submitTransaction( - transaction: InputSubmitTransactionData, + transaction: InputSubmitTransactionData ): Promise { try { this.ensureWalletExists(this._wallet); @@ -1078,9 +1095,7 @@ export class WalletCore extends EventEmitter { // Else have the adapter submit the transaction - const aptosConfig = new AptosConfig({ - network: convertNetwork(this.network), - }); + const aptosConfig = getAptosConfig(this._network, this._dappConfig); const aptos = new Aptos(aptosConfig); if (additionalSignersAuthenticators !== undefined) { const multiAgentTxn = { @@ -1111,7 +1126,7 @@ export class WalletCore extends EventEmitter { await this.setAnsName(); this.recordEvent("account_change"); this.emit("accountChange", this._account); - }, + } ); } catch (error: any) { const errMsg = generalizedErrorMessage(error); @@ -1132,7 +1147,7 @@ export class WalletCore extends EventEmitter { this.setNetwork(data); await this.setAnsName(); this.emit("networkChange", this._network); - }, + } ); } catch (error: any) { const errMsg = generalizedErrorMessage(error); @@ -1170,7 +1185,7 @@ export class WalletCore extends EventEmitter { return response.args; } throw new WalletChangeNetworkError( - `${this._wallet.name} does not support changing network request`, + `${this._wallet.name} does not support changing network request` ).message; } catch (error: any) { const errMsg = generalizedErrorMessage(error); @@ -1192,14 +1207,14 @@ export class WalletCore extends EventEmitter { if (this._wallet.isAIP62Standard) { return this.walletStandardCore.signMessageAndVerify( message, - this._wallet, + this._wallet ); } return await this.walletCoreV1.signMessageAndVerify( message, this._wallet, - this._account, + this._account ); } catch (error: any) { const errMsg = generalizedErrorMessage(error); diff --git a/packages/wallet-adapter-core/src/utils/helpers.ts b/packages/wallet-adapter-core/src/utils/helpers.ts index 21514cd0..0b8412f1 100644 --- a/packages/wallet-adapter-core/src/utils/helpers.ts +++ b/packages/wallet-adapter-core/src/utils/helpers.ts @@ -10,6 +10,7 @@ import { import { NetworkInfo as StandardNetworkInfo } from "@aptos-labs/wallet-standard"; import { convertNetwork } from "../LegacyWalletPlugins/conversion"; import { NetworkInfo } from "../LegacyWalletPlugins/types"; +import { DappConfig } from "../WalletCore"; export function isMobile(): boolean { return /Mobile|iP(hone|od|ad)|Android|BlackBerry|IEMobile|Kindle|NetFront|Silk-Accelerated|(hpw|web)OS|Fennec|Minimo|Opera M(obi|ini)|Blazer|Dolfin|Dolphin|Skyfire|Zune/i.test( @@ -64,10 +65,12 @@ export const areBCSArguments = ( * Helper function to get AptosConfig that supports Aptos and Custom networks * * @param networkInfo + * @param dappConfig * @returns AptosConfig */ export const getAptosConfig = ( - networkInfo: NetworkInfo | StandardNetworkInfo | null + networkInfo: NetworkInfo | StandardNetworkInfo | null, + dappConfig: DappConfig | undefined ): AptosConfig => { if (!networkInfo) { throw new Error("Undefined network"); @@ -75,11 +78,13 @@ export const getAptosConfig = ( if (isAptosNetwork(networkInfo)) { return new AptosConfig({ network: convertNetwork(networkInfo), + clientConfig: { API_KEY: dappConfig?.aptosApiKey }, }); } return new AptosConfig({ network: Network.CUSTOM, fullnode: networkInfo.url, + clientConfig: { API_KEY: dappConfig?.aptosApiKey }, }); }; diff --git a/packages/wallet-adapter-react/README.md b/packages/wallet-adapter-react/README.md index bebb6f70..c137dbc5 100644 --- a/packages/wallet-adapter-react/README.md +++ b/packages/wallet-adapter-react/README.md @@ -75,7 +75,7 @@ const wallets = [new AptosLegacyStandardWallet()]; plugins={wallets} autoConnect={true} optInWallets={["Petra"]} - dappConfig={{ network: network.MAINNET }} + dappConfig={{ network: network.MAINNET, aptosApiKey: "my-generated-api-key" }} onError={(error) => { console.log("error", error); }} @@ -86,12 +86,14 @@ const wallets = [new AptosLegacyStandardWallet()]; #### Available Provider Props +- `dappConfig` - Config used to initialize the dapp with. + - `network` - the network the dapp works with + - `aptosApiKey` - an api key generated from https://developers.aptoslabs.com/docs/api-access +- `onError` - a callback function to fire when the adapter throws an error - `plugins` - any legacy standard wallet, i.e a wallet that is not AIP-62 standard compatible, should be installed and passed in this array. [Check here](../../README.md#supported-wallet-packages) for a list of AIP-62 and legacy standard wallets. - `autoConnect` - a prop indicates whether the dapp should auto connect with a previous connected wallet. - `optInWallets` - the adapter detects and adds AIP-62 standard wallets by default, sometimes you might want to opt-in with specific wallets. This props lets you define the AIP-62 standard wallets you want to support in your dapp. -- `dappConfig` - the adapter comes built-in with AIP-62 standard SDK wallets and it needs to know what configuration your dapp is in to render the current instance. -- `onError` - a callback function to fire when the adapter throws an error -- (optional) `disableTelemetry` - A boolean flag to disable the adapter telemetry tool, false by default +- `disableTelemetry` - A boolean flag to disable the adapter telemetry tool, false by default #### Use Wallet diff --git a/turbo.json b/turbo.json index eefe30e0..507788ce 100644 --- a/turbo.json +++ b/turbo.json @@ -1,5 +1,6 @@ { "$schema": "https://turbo.build/schema.json", + "globalEnv": ["NEXT_PUBLIC_APTOS_API_KEY"], "tasks": { "build": { "dependsOn": ["^build"],