:::note This tutorial walks through how to build an application with the [`@stellar/stellar-sdk`]. To build with the Wallet SDK, please follow the [Build a Wallet tutorial](https://developers.stellar.org/docs/build/apps/wallet/overview.md). To build with smart contracts, navigate to the [Smart Contracts section](https://developers.stellar.org/docs/build/smart-contracts/getting-started/setup.md). ::: In this tutorial, we'll walk through the steps needed to build a basic payment application on Stellar's Testnet. After this tutorial, you should have a good understanding of the fundamental Stellar concepts and a solid base for iterative development. For this tutorial, we'll walk through the steps as we build a sample application we've called [BasicPay], which will be used to showcase various features. :::caution Although BasicPay is a full-fledged application on Stellar's Testnet, it has been built solely to showcase Stellar functionality for the educational purposes of this tutorial, not to be copied, pasted, and used on Mainnet. ::: ## Project Setup ### Project Requirements To build a basic Stellar application, you'll need: - Application framework: we're using [SvelteKit] opting for JavaScript with JSDoc typing. SvelteKit is quite flexible and could be used for a lot, but we are mainly using it for its routing capabilities to minimize this being a "SvelteKit tutorial". - Frontend framework: we're using [DaisyUI] to simplify the use of [Tailwind CSS]. - A way to interact with the Stellar network: we're using the [`@stellar/stellar-sdk`], but you could use traditional fetch requests. The [`@stellar/stellar-sdk`] is also used for building transactions to submit to the Stellar network. - A way to interact with the user's keypair: we're using [`@stellar/typescript-wallet-sdk-km`], but you can opt to use an existing wallet. :::note While we are using the above components to construct our application, we have done our best to write this tutorial in such a way that dependency on any one of these things is minimized. Ideally, you should be able to use the JavaScript code we've written and plug it into any other framework you'd like with minimal effort. ::: We've made the following choices during the development of BasicPay that you may also need to consider as you follow along: - We've designed this app for desktop. For the most part, the app is responsive to various screen sizes, but we have _chosen_ not to go out of our way to prioritize the mobile user experience. - We have enabled the default DaisyUI "light" and "dark" themes, which should switch with the preferences of your device. There is no toggle switch enabled, though. - This is written as a client-side application. No server-side actions actually take place. If you are building an application with a backend and frontend, you will need to consider carefully which information lives where, especially when a user's secret key is involved. - We're deploying this as a static "single-page application" with [Cloudflare Pages]. Your own deployment decisions will have an impact on your configuration and build process. - The application is likely not as performant as it could be. Neither is it as optimized as it could be. We've tried to encapsulate the various functionalities in a way that makes sense to the developer reading the codebase, so there is some code duplication and things _could_ be done in a "better" way. - We do _some_ error handling, but not nearly as much as you would want for a real-world application. If something seems like it's not working, and you're not seeing an error, open your developer console, and you might be able to figure out what has gone wrong. - We have not implemented _any_ automated testing. You'll probably want some for your application. :::note This tutorial is probably best viewed as "_nearly_ comprehensive." We aren't going to walk you through each and every file in our codebase, and the files we do use to illustrate concepts in the tutorial may not be _entirely_ present or explained. However, we will cover the basics, and point you to more complete examples in the codebase when applicable. ::: ### Dev Helpers - [Stellar Lab]: an experimental playground to interact with the Stellar network - Friendbot: a bot that funds accounts with 10,000 fake XLM on Stellar's Testnet; you can fund your account by going to `https://friendbot.stellar.org/?addr=G...` - [Testnet toml file]: an example `stellar.toml` file that demonstrates what information an anchor might publish - [BasicPay dev helpers]: if you're _using_ the BasicPay application, we've created a few helpful tools to help you explore its functionality ## Getting Started Here are the steps we've taken to start building BasicPay. Feel free to be inspired and customize these directions as you see fit. The entire [BasicPay codebase] is freely open and available on GitHub for reference. :::note This part of the tutorial will need a large helping of "your mileage may vary." We will outline what steps we've taken for our deployment situation, but you will want to review what options are needed for your environment(s). ::: ### Install Frameworks The first thing we'll need to do is create a SvelteKit app, using `npm`, we are using v18.x of nodejs. ```bash npx sv create my-basic-payment-app ``` This will walk you through the SvelteKit creation process, asking you about the various options. We've chosen the following options: - **Which Svelte app template?** Skeleton project - **Are you type checking with TypeScript?** Yes, using JavaScript with JSDoc comments - **Select additional options** Add ESLint for code linting; Add Prettier for code formatting After this process, the scaffolding for your application will live inside the `my-basic-payment-app` directory. You can `cd` into that directory and add some UI dependencies. ```bash npm2yarn cd my-basic-payment-app npm install --save-dev svelte-preprocess tailwindcss@^3.4.13 autoprefixer postcss @sveltejs/adapter-static \ @tailwindcss/typography \ daisyui svelte-feather-icons ``` Before we configure anything, we'll need to generate our `tailwind.config.js` and `postcss.config.js` files. ```bash npx tailwindcss init -p ``` Now, we will require a bit of configuration to make all those components work together. First, modify your `svelte.config.js` file: ```js title="/svelte.config.js" // highlight-start import preprocess from "svelte-preprocess"; import adapter from "@sveltejs/adapter-static"; // highlight-end /** @type {import('@sveltejs/kit').Config} */ const config = { kit: { // Note: Your `adapter` configuration may need customizations depending // on how you are building and deploying your application. // highlight-start adapter: adapter({ fallback: "index.html", }), // highlight-end }, // highlight-start preprocess: [ preprocess({ postcss: true, }), ], // highlight-end }; export default config; ``` Next, you can configure the `tailwind.config.js` file. 1. Import the `daisyui` and `typography` plugins 2. Configure our content paths (you may need to modify these values depending on your project structure) 3. Add the `daisyui` plugin **after** any officialy `@tailwindcss` plugins (only `typography` in our example) ```js title="/tailwind.config.js" // highlight-start const daisyui = require("daisyui"); const typography = require("@tailwindcss/typography"); // highlight-end /** @type {import('tailwindcss').Config} */ export default { content: [ // highlight-start "./src/routes/**/*.{html,js,svelte,ts}", "./src/routes/**/**/*.{html,js,svelte,ts}", "./src/lib/components/**/*.{html,js,svelte,ts}", // highlight-end ], // highlight-next-line plugins: [typography, daisyui], }; ``` Add your tailwind directives to your app's main CSS file. ```css title="/src/app.postcss" @tailwind base; @tailwind components; @tailwind utilities; ``` Then import the CSS file into your base SvelteKit layout (you may need to create this file). ```html title="/src/routes/+layout.svelte" ``` We also created a `/src/routes/+layout.js` file to configure our application as _only_ client-side. This means the app will be delivered to the client as unrendered HTML and JavaScript. ```js title="/src/routes/+layout.js" // Disable pre-rendering of pages during build-time export const prerender = false; // Disable server-side rendering export const ssr = false; ``` Your SvelteKit project is now configured and ready to run! ```bash npm2yarn npm run dev ``` ### Stellar dependencies To work with the Stellar network, datastructures, and locally stored keypairs, we're going to install and configure a few more dependencies. ```bash npm2yarn # Stellar SDKs npm install @stellar/stellar-sdk @stellar/typescript-wallet-sdk-km # Wallet integration packages (required for vite.config.js SSR bundling) npm install @creit.tech/stellar-wallets-kit@^1 @stellar/freighter-api @lobstrco/signer-extension-api # We will need some polyfills to make things available client-side npm install --save-dev @esbuild-plugins/node-globals-polyfill @esbuild-plugins/node-modules-polyfill \ path @rollup/plugin-inject buffer svelte-local-storage-store uuid ``` :::info[Stellar Wallets Kit v1] The Stellar Wallets Kit dependency is pinned to `@^1` on purpose. This tutorial and its companion [`stellar/basic-payment-app`](https://github.com/stellar/basic-payment-app) are written against the Kit v1 API (`new StellarWalletsKit(...)`). Installing the package unpinned now resolves to v2.x, whose initialization API is incompatible. This pin keeps the tutorial working until it and BasicPay are migrated to v2 together. ::: We will use a `window.js` file to inject Buffer into our client-side code, since that's required by some parts of the `@stellar/stellar-sdk`. ```js title="/src/lib/window.js" import { browser } from "$app/environment"; import { Buffer } from "buffer"; if (browser) { window.Buffer = Buffer; } else { globalThis.Buffer = Buffer; globalThis.window = {}; } export default globalThis; ``` The actual "injection" takes place in our `vite.config.js` file. ```js title="/vite.config.js" import { sveltekit } from "@sveltejs/kit/vite"; import { defineConfig } from "vite"; import { NodeGlobalsPolyfillPlugin } from "@esbuild-plugins/node-globals-polyfill"; import inject from "@rollup/plugin-inject"; import path from "path"; export default defineConfig({ plugins: [sveltekit()], optimizeDeps: { esbuildOptions: { define: { global: "globalThis", }, plugins: [ NodeGlobalsPolyfillPlugin({ buffer: true, }), ], }, }, build: { rollupOptions: { plugins: [ inject({ window: path.resolve("src/lib/window.js"), }), ], }, }, ssr: { noExternal: [ "@creit.tech/stellar-wallets-kit", "@stellar/freighter-api", "@lobstrco/signer-extension-api", ], }, }); ``` That should take care of everything you need! If you've followed these steps, you now have running client-side-only application that's ready to build out an application that interacts with the Stellar network! Way to go! Next up, we'll look at how we register a user and create their account on the Stellar network. [basicpay]: https://basicpay.pages.dev [sveltekit]: https://kit.svelte.dev [daisyui]: https://daisyui.com [tailwind css]: https://tailwindcss.com [`@stellar/stellar-sdk`]: https://www.npmjs.com/package/@stellar/stellar-sdk [`@stellar/typescript-wallet-sdk-km`]: https://www.npmjs.com/package/@stellar/typescript-wallet-sdk-km [cloudflare pages]: https://pages.cloudflare.com/ [stellar lab]: https://lab.stellar.org [testnet toml file]: https://testanchor.stellar.org/.well-known/stellar.toml [basicpay codebase]: https://github.com/stellar/basic-payment-app [basicpay dev helpers]: https://basicpay.pages.dev/dashboard/settings/dev