src/components/wallet/
, so head over there now. We’re going to use the StellarSdk.Keypair.random()
from the StellarSdk to generate a new valid Bantu keypair. That's the easy part. The hard work will be storing that vital information in a secure yet accessible manner.localStorage
. On page reload, we’ll fetch the publicKey
to “login” the user, but for any protected action such as “Copy Secret”, the modal will pop back up asking for the original pincode.prompt
functionality with our own new, more powerful component. First things first we should generate a new component:bantu-prompt
, and deselect both test files leaving only the styling. Once you have that, open src/components/prompt/
and rename the .css
file to .scss
. Fill that style file with this:prompt.tsx
contents with this.lodash-es
. Let’s make sure we’ve got that imported before moving forward:<bantu-prompt prompter={this.prompter} />
component elsewhere in our project. It's worth noting the variables available to us in the prompter
property.show
, message
and placeholder
. The last two—resolve
and reject
—are for promisifying the prompt so we can await a response before continuing with further logic. Don't worry: that statement will make more sense in a moment once we include this component in src/components/wallet/
. Speaking of, let’s swing over to that component now.bantu-wallet
project before running this string of commands:../../../
paths and just use @{alias}/{path?}/{module}
. In order to get this past both the linter and compiler we’ll need to modify a couple files.tsconfig.json
file to include these values in the compilerOptions
object.package.json
file to include a _moduleAliases
key at the root of the object.module-alias
package and add it to the top of the src/index.ts
file.interface
is just the TypeScript way of setting up a tidy typed class. BantuAccount
will be our account class. It includes the publicKey
for easy reference later in Horizon or Astrograph calls and the Top Secret keystore
key containing the encrypted account secret cipher.@Component
with its defining values and initializing with some @State
and @Prop
data. You can see we’re setting up an account
state with our BantuAccount
class as well as a prompter
state with that Prompter
class from the bantu-prompt
we imported earlier. We’re initializing that prompter
state with a show
value of false
so the prompt modal rendereth not initially../events/componentWillLoad.ts
componentWillLoad
is the Stencil way of pre-filling the state and prop values before actually rendering the component. In our case we’ll use this method to populate the account
@State
with the saved storage keyStore
value if there is one. At first there won’t be, so we’ll come back to this once we’ve actually gone over how to create and save accounts. For now just know it’s here, and since you’re smart, I imagine you can already kind of see how it works.@services/error
and @services/storage
packages?” Fine, yes, we should go over those. Remember the module alias stuff from earlier? Well one was for @prompt
and the other was for @services
. Go ahead and create these two files and add them to the src/services
directory.error.ts
will look like this.storage.ts
.@capacitor/core
. Let’s install and set that up.componentWillLoad
event. On to the ./events/render.tsx
file..tsx
file rendering out our DOM based off a series of conditional values. You can see we’re including the bantu-prompt
component, and setting the prompter prop to our this.prompter
state. We then have a ternary operation toggling between a Create Account button and a basic account UI. If this.account
has a truthy value, we’ll print out the account’s publicKey
along with some interaction buttons. If this.account
is falsey, we’ll print out a singular Create Account button connected to, you guessed it, the createAccount
method. After that logic, we print out an error if there is one, and finally a Sign Out button if there’s an account to sign out of. Those are the two Wallet
@Component
events../methods/createAccount.ts
file.@tinyanvil/sjcl
package.sjcl.encrypt
method to encrypt the secret key from the Keypair.random()
method. We set the this.account
with the publicKey
, which encrypted the keystore
cipher, and now we're storing that cipher in base64 format in localStorage
via our set('keyStore')
method for easy retrieval when the browser reloads. We could also encode that cipher into a QR code or a link to share with other devices. Since it requires the pincode that encrypted cipher, it's as secure as the pincode you encrypt it with.copyAddress
, copySecret
, and signOut
../methods/copyAddress.ts
copy
the publicKey
from the this.account
object to the clipboard. Before we jump though don’t forget to install that copy-to-clipboard
package../methods/copySecret.ts
createAccount
in reverse: it asks for the pincode to decrypt the keystore which, once decrypted, we copy
into the clipboard../methods/signOut.ts
setPrompt
. Once they opt to “NUKE” the account we can remove the keyStore
and reload the app.setPrompt
the last method in our wallet.ts
file is ./methods/setPrompt.ts
.setPrompt
, we see how the prompt state is set, and how the Promise is set up to allow us to wait on the prompt whenever we call this method. It’s actually pretty slick, and it might be worth looking back at the src/components/prompt/prompt.tsx
to see how the resolve
and reject
functions get called. It’s not central to our wallet creation, but it’s a pretty handy little component that will serve us well in the future as we continue to request input from the user.npm start
and you’ve got a perfectly legitimate, minimal Bantu wallet key creation and storage Web Component! It's a solid foundation for a non-custodial wallet that relies on a simple pincode.