Wallet dApp API Standards

Currently, different wallets in the community have different APIs that dApps can call. This makes it more complicated for adapters and dApps to support multiple Aptos’ wallets without having to learn all of them and implement things separately for each wallet.

I’m proposing a shared schema for wallets to conform to. Wallets can have more specialized API’s on top of these but these can be the common ones supported. Would love to have any discussion on if/what we should change here.

Connection API

  • connect() => Promise<PublicAccount>
  • disconnect() => Promise<void>
  • isConnected() => Promise<boolean>

Transaction API

  • signAndSubmitTransaction(transaction: Transaction) => Promise<PendingTransaction>
  • signTransaction(transaction: Transaction) => Promise< SubmitTransactionRequest>
  • signMessage(message: string) => Promise<string>

Misc API

  • account() => Promise<PublicAccount>

Types

  • PublicAccount
    • address: string
    • publicKey: string
  • UserTransaction
  • SubmitTransactionRequest
  • PendingTransaction
    • type: string
    • hash: string
    • Github
11 Likes

In addition to the above outlined by Kent, may I suggest the following:

  1. Wallets should avoid injecting to window.aptos so as to avoid conflicting with each other. For example, our hippo wallet uses window.hippo, the martian wallet uses window.martian, etc.
  2. Wallets may provide name, url, and icon in the object they inject to window, so frontends/adapters know exactly how to display your wallet.

In our aptos-wallet-adapter, we use something like this:

export abstract class BaseWalletAdapter
  extends EventEmitter<WalletAdapterEvents>
  implements WalletAdapter
{
  abstract name: string;
  abstract url: string;
  abstract icon: string;
  abstract get account(): Account | null;
  get connected(): boolean {
    return !!this.account;
  }
  abstract connect(): Promise<void>;
  abstract disconnect(): Promise<void>;
  abstract signAndSubmitTransaction(transaction: TransactionPayload): Promise<PendingTransaction>;
  abstract signTransaction(transaction: TransactionPayload): Promise<SubmitTransactionRequest>;
}
5 Likes

Looking good as a start point to have kind of standartization around all wallet apps.

May I ask why shall we return Aptos Account as part of this project? Can it potentially be a security breach if an App can get account object over API and potentially can do all transactions without being confirmed by User? My view on this topic is that signAndSubmitTransaction is a good starting point but not beyond. This way, we will be certain that it goes through confirmation by User within the Wallet first and Account object remains without exposure to third party website. Just something to consider.

2 Likes

At @fewcha/web3, we define the ProviderStandard must be followed:

We can see the difference is to connect and disconnect return void, cause this action cannot be synchronized because it has to wait for the user to confirm at the wallet. So, it’s an asynchronous operation and must be checked later with the isConnected function.

Also, we have defined some events that the client can catch these actions:

And a provider should support SDK functions (Fewcha wallet supported).

to differentiate between providers, or avoid interchangeability, as we raised before.

We should use window.aptos.<provider-codename> instead of using window.abc directly. And add a variable to distinguish in case the key cannot be determined at , then an additional is<provider-codename> is required as Kent suggested,

window.aptos.<provider>
window.aptos.<provider>.is<provider>

# Example

window.aptos.fewcha
window.aptos.isFewcha
3 Likes

an it potentially be a security breach if an App can get account object over API and potentially can do all transactions without being confirmed by User

2 things that prevent the security risk.

  1. A user should be prompted before signing anything.
  2. In Aptos Labs wallet, you need to first connect() before calling other functions like account(). This prevents malicious dapps from sending unwanted requests to the wallet.
1 Like

We should use window.aptos. instead of using window.abc directly.

If you want to avoid colliding with window.aptos you could just use window.fewcha instead of window.aptos.fewcha :slight_smile:

So, it’s an asynchronous operation and must be checked later with the isConnected function.

connect returns a Promise so it is asynchronous. You can wait for the user to respond to the prompt and then return the result.

1 Like

Got it, thanks for explanation. Correct me if I’m wrong, but still not 100% certain if returning Account object is something that has to be part of it, mainly due to it involves sending private key over the network, but calling for signAndSubmitTransaction and waiting for User to confirm it inside the wallet will return transaction signature to dApp. That way transaction is being initiated by dApp, then confirmed by User and signed by wallet, and result returned to dApp without private key leaving the wallet. Seems like a safer approach.

Is there a consensus as to which wallet Aptos will endorse as the suggested wallet?

I have the same question, I’m creating a popup to select a wallet that looks like Solana

Or web3modal

The popup will be available in @fewcha/web3-react in the next few days

3 Likes

Got it! I will include that changes in the next version of Fewcha (v0.3.2)

2 Likes

In my Dart SDK I also found myself wanting a function like signAndSubmitAndWaitTransaction: aptos_sdk_dart/aptos_client_helper.dart at main · banool/aptos_sdk_dart · GitHub. I wonder if we should offer something like that too.

This would necessitate returning some kind of complex Result struct, which we’d have to define as part of this too.

It seems unwieldy but this is what everyone is doing, I wonder if it’s worth including.

1 Like

Account returned by the wallet is only the { address, publicKey }. The private key should never be accessible to the dapp.

1 Like

It’s not the same Account object from the TS SDK. Only address & publicKey.

2 Likes

Another thing that could be nice is to have common types for certain things, e.g. address, publicKey, etc, similar to newtypes in Rust. It might be a tough sell to get all wallet creators to use these types though, but perhaps easier if we’re just re-exposing types from within the SDK.

1 Like

Alright, it’s much clear now. So what we actually sending with Account is publicKey and account address so dApp can prepare transaction payload for submission back to wallet. That is not what I initially though as @magnum6 has mentioned before it use the same naming as Aptos Account in TS SDK which is keypair + authKey. Perhaps, continue following SDK naming as suggested by @dport is a great idea to keep standard structure. In this case, it could be worth to reserve all function names used in SDK and rename Account in this current context to something like UserIdentity (just an example) to avoid any confusion in future.

1 Like

That’s a good idea. I’ll call it PublicAccount

1 Like

Should we have?

  • submitTransaction(signedTxnRequest:SubmitTransactionRequest) => Promise<Types.PendingTransaction> (for result of signTransaction)
  • simulateTransaction(txnRequest: Types.UserTransactionRequest): Promise<Types.OnChainTransaction> (for client can estimate gas before submit).

And

Should we have?

  • getNodeURL() => Promise<string>;
  • getBalance() => Promise<string>;

Also, we can have:

sdk: { ... /* with get functions of SDK */ };

Look like:

sdk: {
   getAccountTransactions(
      accountAddress: MaybeHexString,
      query?: { start?: number; limit?: number },
   ): Promise<Types.OnChainTransaction[]> 
   // ...
}

About Transaction API, We should support functions of BCS too? and generateTransaction functions.

  • `submitTransaction and simulateTransaction

My opinion here is that those functions are directly accessible via the sdk, and a dapp should probably be running it’s own instance of the sdk rather than us having a wrapper for everything.

getNodeURL() => Promise; and getBalance() => Promise;

For node url, yes we should have something like this, good idea. Though I think it should be more like getNetwork, because a dapp more cares about if the wallet is on mainnet or not.

For getBalance, it should also be handled by the sdk.

Interested in hearing other arguments here though.

1 Like

I know you want the user to use the SDK, but the wallet cannot give the AptosAccount directly to the DApp (for security reasons) so the functions that use the AptosAccount must be forwarded

1 Like