ethsign-keychain-api
v1.0.3
Published
`ethsign-keychain-api` is a library for interacting with the EthSign Keychain MetaMask Snap.
Downloads
3
Readme
ethsign-keychain-api
ethsign-keychain-api
is a library for interacting with the EthSign Keychain MetaMask Snap.
Installation
Run npm i ethsign-keychain-api
or yarn add ethsign-keychain-api
.
Usage
import useKeychain from "ethsign-keychain-api";
function ReactApp() {
const {
connectSnap,
getSnap,
getPassword,
setPassword,
removePassword,
sync,
setNeverSave,
encrypt,
decrypt,
exportState,
importState,
getUrl,
getUserRegistry,
getSyncTo,
setSyncTo
} = useKeychain();
...
return <>...</>;
}
useKeychain()
will automatically call connectSnap()
once. If the MetaMask popup is rejected, you will need to call connectSnap()
again to use other EthSign Keychain functionality.
NOTE - Troubleshooting Crashes
The MetaMask Snaps API supports showing one popup at a time. If your code invokes the snap more than once (or before previous calls have completed), MetaMask may crash and all popups will disappear, even if the first one was already showing. Snap RPCs are not blocking within MetaMask. These snap crashes may not be present if the user has already granted appropriate access permissions, but they may be present on a new install (or where access permissions for your site have not been already granted).
For instance, calling setPassword()
immediately followed by getPassword()
where the user has not previously granted your site permission to their credential store will likely cause a crash: setPassword()
will request access permissions through a MetaMask popup and the user will not have time to interact with the popup before getPassword()
requests access permissions again. This will attempt to show two popups at the same time, and MetaMask will terminate the Keychain snap.
This can also be an issue with React.StrictMode
enabled in your development environment if your function call to the snap results in a popup. React.StrictMode
will render components twice, which means any useEffect()
calls will run twice in immediate succession. This can result in MetaMask attempting to show two popups at the same time, which will crash the snap even if you only called the respective function once from within a useEffect()
.
Methods
connectSnap(params: Record<"version" | string, unknown> = {}): Promise<Response<any>>
Prompts MetaMask to install the EthSign Keychain snap. Version can be a string corresponding to a version of the EthSign Keychain Snap. Left blank, it will install the latest version.
...
const SNAP_VERSION: "0.2.6";
await connectSnap({ version: SNAP_VERSION });
...
getSnap(version: string): Promise<any>
Looks for the EthSign Keychain snap in the installed list of snaps on the user's MetaMask installation. Returns undefined if it cannot be found, or it returns the snap information if it is installed.
...
const SNAP_VERSION: "0.2.6";
await getSnap(SNAP_VERSION);
...
getPassword(url: string = getUrl()): Promise<Response<EthSignKeychainPasswordState>>
Gets the password state for a given URL. url
defaults to the current URL.
...
const url = "https://ethsign.xyz";
await getPassword(url);
...
setPassword(username: string, password: string, url: string = getUrl(), controlled: boolean = true): Promise<Response<any>>
Set/update the password state for a given URL, username, and password combination. Controlled marks that this credential is controlled by an API and not created by a user - this will help the user realize that some credential entries should not be manually touched.
...
const url = "https://ethsign.xyz";
const username = "uname";
const password = "Password123";
const controlled = true;
await setPassword(username, password, url, controlled);
...
removePassword(url: string = getUrl(), username: string): Promise<Response<any>>
Remove a password entry from a URL's state given its corresponding username. url
defaults to the current URL.
...
const url = "https://ethsign.xyz";
const username = "uname";
await removePassword(url, username);
...
sync(): Promise<Response<any>>
Sync the local Snap's password state with remote Arweave state.
...
await sync();
...
setNeverSave(url: string = getUrl(), neverSave: string): Promise<Response<any>>
Set whether or not we should save passwords for a given url. url
defaults to the current URL.
...
const url = "https://ethsign.xyz";
const neverSave = false;
await setNeverSave(url, neverSave);
...
encrypt(data: string): Promise<Response<string>>
Encrypt a string using a receiver's public key. Fails if receiver's public key cannot be located.
...
const address = "0x985Eb8f653Ab087d4122F0C1dBc7972dF6B1642B";
const str = "Secret message.";
await encrypt(address, str);
...
decrypt(address: string, data: string): Promise<Response<string>>
Decrypts a hex string using the current user's private key.
...
const data = "04a487f5222b8423863b79424f28ddb324d825bd9d75cd7d5411ab925a53ba391e9512030a9168333bbade7038fa9411f2ac2b5e4f3b6d2109c1be5ef4a4175f4daad3c1556cb97bd2c7b62f77fd5bc0b628eb3d82ca68337520038bfba4ac7fe72dcf1497fea656757a58d0c2856d";
await decrypt(data);
...
exportState(): Promise<Response<string>>
Export the password state from the current user's EthSignKeychainState stored locally. Requires user to enter a password for encryption.
...
await exportState();
...
importState(data: string): Promise<Response<string>>
Import a user's password state, which was encrypted and exported into a JSON object containing nonce and data strings.
...
const state = {
"success": true,
"data": "{\"nonce\":\"M0Z4ILxRGNgdXqttekhThAvVXAYtQcRk\",\"data\":\"UyECrNKhnvzl6rBfrrgtkY8K+baBtSUqTT3kzTybxs6UHlgi8nYS3LArJAg+ZfFqEW/7dP+iEmL/TOiAEcNOkKlTBgdAquTg1F9cUaf6IWvZDLCq030j0OOBHIfQiolYDhm57fGgzxqpFXCRqAEMwvR+p4MnDdFAJE1ccXpbvxznofMqsAz0+n40sDz6SFZ6ro1kv5ZzEhkgUWyKblkHXo5fELPjNWXVxY2aBNAMVqqWoy2fFhNJHO60vHkZ18Ac3Y9hxIDLqX4NrSadRKlpwDE4wrve0D3any++vaxHf5froXZvut42V+Y0zX42n0FJPbNlY5/kuPgBM01Z5c/APzkcwjqzeHsQTk8ZOtWYL3+j6xMkFLOCZtjZX4psNFbIUwjwBjlE3MZCmCi1FXZkDaMFzFkk52XVYkz4o1KtoKa13CXA594oFS2i5OTBPrDspE3LHXwVkRdECBW7Q9QeInH6eJ+sIVgbc7ho8BZ3prPxq8Ub4K3XiC/XB7hWxaRe1uBuSiXtVOyX9PH1PJFbnKFhEL3t+eYCNS1W87qVRZvYyfFxQIf6TKN3a/jY9/OM6f8CYVfr7AvIvGxg1520hV5UVJwFwimrcJnEmJAqBRJAxZN/AI++0XqXFbo8lwueISqBy38deOiitX6Zi6cSY+5gDU2ONetOduX5O/8wo/Teu6dFyx9kQ2jDW1F/ceC19Chy0GnEIHdZTMGz3AiPRx5ltnPLNxBOjn+5Mnevo+/DmZqJOwIIqCoKTaUKatdaAE6QFKWvGbfcQvdUZyqfeEQpzsqmbl03rrH3RpSmCqJfMDSg7YmO6fq4sVFGjjQIPEhhplBTrzVWVvPHfDWIAdlrOETMFWS2zch/OD7uZJ9P2pZ0DAA0r2caqXz43cD7BOY=\"}"
};
await importState(JSON.stringify(state));
...
getUrl(): string
Get the current window's base URL for storing passwords.
...
getUrl();
...
getUserRegistry(address: string): Promise<Response<{publicAddress: string, publicKey: string}>>
Get the public key of a user given the user's public address.
...
const address = "0x985Eb8f653Ab087d4122F0C1dBc7972dF6B1642B";
await getUserRegistry(address);
...
getSyncTo(): Promise<string>
Get the remote sync location from the keychain snap.
...
await getSyncTo();
...
setSyncTo(): Promise<string>
Set the remote sync location for the snap. Returns the newly set remote sync location, or the existing location if an exception occurs.
...
const location = "arweave";
await setSyncTo(location);
...