├── .eslintrc.js ├── .github ├── dependabot.yml └── workflows │ ├── codeql.yml │ ├── integration.anchorPlatformTest.yml │ ├── integration.recoveryTest.yml │ ├── npmPublishSdk.yml │ ├── npmPublishSdkBeta.yml │ ├── npmPublishSdkKM.yml │ ├── npmPublishSdkKMBeta.yml │ ├── npmPublishSdkSoroban.yml │ ├── npmPublishSdkSorobanBeta.yml │ ├── playwrightTests.yml │ └── runTests.yml ├── .gitignore ├── .husky └── pre-commit ├── .npmignore ├── @stellar ├── typescript-wallet-sdk-km │ ├── CHANGELOG.MD │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── src │ │ ├── Handlers │ │ │ ├── albedo.ts │ │ │ ├── freighter.ts │ │ │ ├── index.ts │ │ │ ├── ledger.ts │ │ │ ├── plaintextKey.ts │ │ │ └── trezor.ts │ │ ├── Helpers │ │ │ ├── getKeyMetadata.ts │ │ │ ├── index.ts │ │ │ ├── scryptEncryption.ts │ │ │ └── trezorTransformTransaction.ts │ │ ├── Plugins │ │ │ ├── BrowserStorageFacade.ts │ │ │ ├── BrowserStorageKeyStore.ts │ │ │ ├── IdentityEncrypter.ts │ │ │ ├── LocalStorageFacade.ts │ │ │ ├── LocalStorageKeyStore.ts │ │ │ ├── MemoryKeyStore.ts │ │ │ ├── ScryptEncrypter.ts │ │ │ └── index.ts │ │ ├── Types │ │ │ └── index.ts │ │ ├── index.ts │ │ └── keyManager.ts │ ├── test │ │ ├── fixtures │ │ │ └── keys.ts │ │ ├── helpers.test.ts │ │ ├── keyManager.test.ts │ │ ├── pluginTesting.ts │ │ ├── plugins.test.ts │ │ └── tsconfig.json │ ├── tsconfig.json │ └── webpack.config.js ├── typescript-wallet-sdk-soroban │ ├── CHANGELOG.MD │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── src │ │ ├── Helpers │ │ │ ├── formatTokenAmount.ts │ │ │ ├── getInvocationDetails.ts │ │ │ ├── getTokenInvocationArgs.ts │ │ │ ├── index.ts │ │ │ ├── parseTokenAmount.ts │ │ │ └── scValByType.ts │ │ ├── Types │ │ │ └── index.ts │ │ └── index.ts │ ├── test │ │ ├── helpers.test.ts │ │ ├── tsconfig.json │ │ └── utils │ │ │ └── index.ts │ ├── tsconfig.json │ └── webpack.config.js └── typescript-wallet-sdk │ ├── CHANGELOG.MD │ ├── README.md │ ├── babel.config.js │ ├── examples │ ├── helpers │ │ └── ask-question.ts │ ├── sep10 │ │ ├── .env.example │ │ ├── README.md │ │ ├── sep10Server.ts │ │ ├── sep10Wallet.ts │ │ └── well_known │ │ │ └── stellar.toml │ ├── sep12 │ │ ├── .env.example │ │ ├── README.md │ │ └── sep12.ts │ ├── sep24 │ │ ├── .env.example │ │ ├── README.md │ │ └── sep24.ts │ └── tsconfig.json │ ├── jest.e2e.config.js │ ├── jest.integration.config.js │ ├── package.json │ ├── src │ ├── index.ts │ └── walletSdk │ │ ├── Anchor │ │ ├── Sep24.ts │ │ ├── Sep38.ts │ │ ├── Sep6.ts │ │ └── index.ts │ │ ├── Asset │ │ └── index.ts │ │ ├── Auth │ │ ├── AuthHeaderSigner.ts │ │ ├── WalletSigner.ts │ │ └── index.ts │ │ ├── Customer │ │ └── index.ts │ │ ├── Exceptions │ │ └── index.ts │ │ ├── Horizon │ │ ├── Account.ts │ │ ├── AccountService.ts │ │ ├── Stellar.ts │ │ ├── Transaction │ │ │ ├── CommonTransactionBuilder.ts │ │ │ ├── SponsoringBuilder.ts │ │ │ └── TransactionBuilder.ts │ │ └── index.ts │ │ ├── Recovery │ │ ├── AccountRecover.ts │ │ └── index.ts │ │ ├── Server │ │ └── index.ts │ │ ├── Types │ │ ├── anchor.ts │ │ ├── auth.ts │ │ ├── horizon.ts │ │ ├── index.ts │ │ ├── recovery.ts │ │ ├── sep12.ts │ │ ├── sep24.ts │ │ ├── sep38.ts │ │ ├── sep43.ts │ │ ├── sep6.ts │ │ ├── sep7.ts │ │ ├── utils.ts │ │ └── watcher.ts │ │ ├── Uri │ │ ├── Sep7Base.ts │ │ ├── Sep7Pay.ts │ │ ├── Sep7Tx.ts │ │ ├── index.ts │ │ └── sep7Parser.ts │ │ ├── Utils │ │ ├── camelToSnakeCase.ts │ │ ├── extractAxiosErrorData.ts │ │ ├── getResultCode.ts │ │ ├── index.ts │ │ ├── toml.ts │ │ └── url.ts │ │ ├── Watcher │ │ ├── getTransactions.ts │ │ └── index.ts │ │ └── index.ts │ ├── test │ ├── account.test.ts │ ├── accountService.test.ts │ ├── customer.test.ts │ ├── docker │ │ └── docker-compose.yml │ ├── e2e │ │ ├── README.md │ │ └── browser.test.ts │ ├── fixtures │ │ └── TransactionsResponse.ts │ ├── integration │ │ ├── README.md │ │ ├── anchorplatform.test.ts │ │ └── recovery.test.ts │ ├── recovery.test.ts │ ├── sep38.test.ts │ ├── sep6.test.ts │ ├── sep7.test.ts │ ├── server.test.ts │ ├── stellar.test.ts │ ├── transaction.test.ts │ ├── tsconfig.json │ ├── utils │ │ └── index.ts │ └── wallet.test.ts │ ├── tsconfig.json │ └── webpack.config.js ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── babel.config.js ├── docs └── WalletGuide.md ├── jest.config.js ├── package.json ├── prettier.config.js └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ignorePatterns: ["lib/", "node_modules/", "docs/"], 3 | parser: "@typescript-eslint/parser", 4 | parserOptions: { 5 | project: [ 6 | "@stellar/typescript-wallet-sdk/examples/tsconfig.json", 7 | "@stellar/typescript-wallet-sdk/tsconfig.json", 8 | "@stellar/typescript-wallet-sdk/test/tsconfig.json", 9 | "@stellar/typescript-wallet-sdk-km/tsconfig.json", 10 | "@stellar/typescript-wallet-sdk-km/test/tsconfig.json", 11 | "@stellar/typescript-wallet-sdk-soroban/tsconfig.json", 12 | "@stellar/typescript-wallet-sdk-soroban/test/tsconfig.json", 13 | ], 14 | sourceType: "module", 15 | }, 16 | plugins: ["@typescript-eslint", "jsdoc"], 17 | extends: [ 18 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 19 | "plugin:@typescript-eslint/recommended", 20 | "prettier", 21 | ], 22 | rules: { 23 | // Off 24 | "@typescript-eslint/no-unsafe-argument": "off", 25 | "@typescript-eslint/no-floating-promises": "off", 26 | "@typescript-eslint/no-unsafe-assignment": "off", 27 | "@typescript-eslint/no-unsafe-call": "off", 28 | "@typescript-eslint/no-unsafe-member-access": "off", 29 | "@typescript-eslint/no-unsafe-return": "off", 30 | "@typescript-eslint/prefer-regexp-exec": "off", 31 | "@typescript-eslint/restrict-template-expressions": "off", 32 | "@typescript-eslint/no-inferrable-types": "off", 33 | "@typescript-eslint/no-misused-promises": "off", 34 | "@typescript-eslint/no-base-to-string": "off", 35 | "jsdoc/check-indentation": "off", 36 | 37 | // Warn 38 | "jsdoc/check-param-names": "warn", 39 | "jsdoc/require-returns": "warn", 40 | "jsdoc/require-returns-description": "warn", 41 | "jsdoc/require-returns-type": "warn", 42 | "jsdoc/require-param": "warn", 43 | "jsdoc/check-types": "warn", 44 | "jsdoc/require-param-description": "warn", 45 | "jsdoc/require-param-name": "warn", 46 | "jsdoc/require-param-type": "warn", 47 | "jsdoc/require-property": "warn", 48 | "jsdoc/require-property-description": "warn", 49 | "jsdoc/require-property-name": "warn", 50 | "jsdoc/require-property-type": "warn", 51 | "jsdoc/check-property-names": "warn", 52 | "jsdoc/empty-tags": "warn", 53 | 54 | // Error 55 | "@typescript-eslint/no-shadow": "error", 56 | "@typescript-eslint/no-unused-expressions": "error", 57 | "@typescript-eslint/no-var-requires": "error", 58 | "@typescript-eslint/prefer-for-of": "error", 59 | "@typescript-eslint/prefer-function-type": "error", 60 | "@typescript-eslint/prefer-namespace-keyword": "error", 61 | "@typescript-eslint/triple-slash-reference": [ 62 | "error", 63 | { 64 | path: "always", 65 | types: "prefer-import", 66 | lib: "always", 67 | }, 68 | ], 69 | "@typescript-eslint/unified-signatures": "error", 70 | "@typescript-eslint/no-misused-new": "error", 71 | "@typescript-eslint/no-empty-function": "error", 72 | "@typescript-eslint/no-empty-interface": "error", 73 | "jsdoc/check-alignment": "error", 74 | }, 75 | overrides: [ 76 | { 77 | files: ["**/test/**", "@stellar/typescript-wallet-sdk/examples/**"], 78 | rules: { 79 | "@typescript-eslint/no-shadow": "off", 80 | "@typescript-eslint/no-explicit-any": "off", 81 | }, 82 | }, 83 | ], 84 | }; 85 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directories: 5 | - "/" 6 | - "/@stellar/typescript-wallet-sdk-km" 7 | - "/@stellar/typescript-wallet-sdk-soroban" 8 | - "/@stellar/typescript-wallet-sdk" 9 | schedule: 10 | interval: "weekly" 11 | day: "sunday" 12 | time: "02:00" 13 | open-pull-requests-limit: 2 14 | groups: 15 | minor-and-patch: 16 | applies-to: version-updates 17 | update-types: 18 | - "patch" 19 | - "minor" 20 | major: 21 | applies-to: version-updates 22 | update-types: 23 | - "major" 24 | - package-ecosystem: "github-actions" 25 | directory: "/" 26 | schedule: 27 | interval: "weekly" 28 | day: "sunday" 29 | time: "02:00" 30 | open-pull-requests-limit: 2 31 | groups: 32 | all-actions: 33 | applies-to: version-updates 34 | patterns: [ "*" ] -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | schedule: 9 | - cron: '24 21 * * 2' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze (${{ matrix.language }}) 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 360 16 | permissions: 17 | # required for all workflows 18 | security-events: write 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | include: 24 | - language: javascript-typescript 25 | build-mode: none 26 | 27 | steps: 28 | - name: Checkout repository 29 | uses: actions/checkout@v4 30 | 31 | # Initializes the CodeQL tools for scanning. 32 | - name: Initialize CodeQL 33 | uses: github/codeql-action/init@v3 34 | with: 35 | languages: ${{ matrix.language }} 36 | build-mode: ${{ matrix.build-mode }} 37 | 38 | - name: Perform CodeQL Analysis 39 | uses: github/codeql-action/analyze@v3 40 | with: 41 | category: "/language:${{matrix.language}}" 42 | -------------------------------------------------------------------------------- /.github/workflows/integration.anchorPlatformTest.yml: -------------------------------------------------------------------------------- 1 | name: Run Test 2 | on: [pull_request] 3 | jobs: 4 | test-ci: 5 | name: anchor platform test 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - uses: actions/setup-node@v2 10 | with: 11 | node-version: 18 12 | - run: yarn install 13 | - run: yarn test:anchorplatform:ci 14 | -------------------------------------------------------------------------------- /.github/workflows/integration.recoveryTest.yml: -------------------------------------------------------------------------------- 1 | name: Recovery Signer Integration Test 2 | on: [pull_request] 3 | jobs: 4 | test-ci: 5 | name: recovery test 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - name: Start docker 10 | run: 11 | docker compose -f 12 | @stellar/typescript-wallet-sdk/test/docker/docker-compose.yml up -d 13 | - uses: actions/setup-node@v2 14 | with: 15 | node-version: 18 16 | - run: yarn install 17 | - run: yarn build 18 | - run: yarn test:recovery:ci 19 | - name: Print Docker Logs 20 | if: always() # This ensures that the logs are printed even if the tests fail 21 | run: 22 | docker compose -f 23 | @stellar/typescript-wallet-sdk/test/docker/docker-compose.yml logs 24 | -------------------------------------------------------------------------------- /.github/workflows/npmPublishSdk.yml: -------------------------------------------------------------------------------- 1 | name: npm publish wallet sdk 2 | on: [workflow_dispatch] 3 | jobs: 4 | npm-publish: 5 | name: npm-publish 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - uses: actions/setup-node@v2 10 | with: 11 | node-version: 18 12 | registry-url: https://registry.npmjs.org/ 13 | - run: yarn install 14 | - run: yarn build 15 | - run: yarn test:ci 16 | 17 | - name: Publish to NPM 18 | run: | 19 | cd @stellar/typescript-wallet-sdk 20 | yarn publish --access public 21 | env: 22 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 23 | -------------------------------------------------------------------------------- /.github/workflows/npmPublishSdkBeta.yml: -------------------------------------------------------------------------------- 1 | name: typescript-wallet-sdk beta build 2 | on: 3 | push: 4 | branches: 5 | - develop 6 | jobs: 7 | npm-beta: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout repository 11 | uses: actions/checkout@v2 12 | - uses: actions/setup-node@v2 13 | with: 14 | node-version: 18 15 | registry-url: https://registry.npmjs.org/ 16 | - run: yarn install 17 | - run: yarn build 18 | - run: yarn test:ci 19 | 20 | - name: Create beta package version 21 | run: | 22 | timestamp=$(date +%s%3N) 23 | current_version=$(jq -r '.version' @stellar/typescript-wallet-sdk/package.json) 24 | echo "new_version=${current_version}-beta.${timestamp}" >> $GITHUB_ENV 25 | 26 | - name: Update package.json version 27 | uses: jossef/action-set-json-field@6e6d7e639f24b3955ef682815317b5613ac6ca12 # v1 28 | with: 29 | file: ./@stellar/typescript-wallet-sdk/package.json 30 | field: version 31 | value: ${{ env.new_version }} 32 | 33 | - name: Publish beta build 34 | run: | 35 | cd @stellar/typescript-wallet-sdk 36 | yarn publish --tag beta --access public 37 | env: 38 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 39 | -------------------------------------------------------------------------------- /.github/workflows/npmPublishSdkKM.yml: -------------------------------------------------------------------------------- 1 | name: npm publish wallet sdk Key Manager 2 | on: [workflow_dispatch] 3 | jobs: 4 | npm-publish: 5 | name: npm-publish 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - uses: actions/setup-node@v2 10 | with: 11 | node-version: 18 12 | registry-url: https://registry.npmjs.org/ 13 | - run: yarn install 14 | - run: yarn build 15 | - run: yarn test:ci 16 | 17 | - name: Publish to NPM 18 | run: | 19 | cd @stellar/typescript-wallet-sdk-km 20 | yarn publish --access public 21 | env: 22 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 23 | -------------------------------------------------------------------------------- /.github/workflows/npmPublishSdkKMBeta.yml: -------------------------------------------------------------------------------- 1 | name: typescript-wallet-sdk-km beta build 2 | on: 3 | push: 4 | branches: 5 | - develop-km 6 | jobs: 7 | npm-beta: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout repository 11 | uses: actions/checkout@v2 12 | - uses: actions/setup-node@v2 13 | with: 14 | node-version: 18 15 | registry-url: https://registry.npmjs.org/ 16 | - run: yarn install 17 | - run: yarn build 18 | - run: yarn test:ci 19 | 20 | - name: Create beta package version 21 | run: | 22 | timestamp=$(date +%s%3N) 23 | current_version=$(jq -r '.version' @stellar/typescript-wallet-sdk-km/package.json) 24 | echo "new_version=${current_version}-beta.${timestamp}" >> $GITHUB_ENV 25 | 26 | - name: Update package.json version 27 | uses: jossef/action-set-json-field@6e6d7e639f24b3955ef682815317b5613ac6ca12 # v1 28 | with: 29 | file: ./@stellar/typescript-wallet-sdk-km/package.json 30 | field: version 31 | value: ${{ env.new_version }} 32 | 33 | - name: Publish beta build 34 | run: | 35 | cd @stellar/typescript-wallet-sdk-km 36 | yarn publish --tag beta --access public 37 | env: 38 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 39 | -------------------------------------------------------------------------------- /.github/workflows/npmPublishSdkSoroban.yml: -------------------------------------------------------------------------------- 1 | name: npm publish wallet sdk Soroban 2 | on: [workflow_dispatch] 3 | jobs: 4 | npm-publish: 5 | name: npm-publish 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - uses: actions/setup-node@v2 10 | with: 11 | node-version: 18 12 | registry-url: https://registry.npmjs.org/ 13 | - run: yarn install 14 | - run: yarn build 15 | - run: yarn test:ci 16 | 17 | - name: Publish to NPM 18 | run: | 19 | cd @stellar/typescript-wallet-sdk-soroban 20 | yarn publish --access public 21 | env: 22 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 23 | -------------------------------------------------------------------------------- /.github/workflows/npmPublishSdkSorobanBeta.yml: -------------------------------------------------------------------------------- 1 | name: typescript-wallet-sdk-soroban beta build 2 | on: 3 | push: 4 | branches: 5 | - develop-soroban 6 | jobs: 7 | npm-beta: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout repository 11 | uses: actions/checkout@v2 12 | - uses: actions/setup-node@v2 13 | with: 14 | node-version: 18 15 | registry-url: https://registry.npmjs.org/ 16 | - run: yarn install 17 | - run: yarn build 18 | - run: yarn test:ci 19 | 20 | - name: Create beta package version 21 | run: | 22 | timestamp=$(date +%s%3N) 23 | current_version=$(jq -r '.version' @stellar/typescript-wallet-sdk-soroban/package.json) 24 | echo "new_version=${current_version}-beta.${timestamp}" >> $GITHUB_ENV 25 | 26 | - name: Update package.json version 27 | uses: jossef/action-set-json-field@6e6d7e639f24b3955ef682815317b5613ac6ca12 # v1 28 | with: 29 | file: ./@stellar/typescript-wallet-sdk-soroban/package.json 30 | field: version 31 | value: ${{ env.new_version }} 32 | 33 | - name: Publish beta build 34 | run: | 35 | cd @stellar/typescript-wallet-sdk-soroban 36 | yarn publish --tag beta --access public 37 | env: 38 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 39 | -------------------------------------------------------------------------------- /.github/workflows/playwrightTests.yml: -------------------------------------------------------------------------------- 1 | name: Playwright Tests 2 | on: [pull_request] 3 | jobs: 4 | playwright: 5 | name: "Playwright e2e Tests" 6 | runs-on: ubuntu-latest 7 | container: 8 | image: mcr.microsoft.com/playwright:v1.43.0-jammy 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions/setup-node@v2 12 | with: 13 | node-version: 18 14 | - run: yarn install 15 | - run: yarn build 16 | - run: yarn test:e2e:ci 17 | -------------------------------------------------------------------------------- /.github/workflows/runTests.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests 2 | on: [pull_request] 3 | jobs: 4 | test-ci: 5 | name: test 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - uses: actions/setup-node@v2 10 | with: 11 | node-version: 18 12 | - run: yarn install 13 | - run: yarn build 14 | - run: yarn test:ci 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | */**/node_modules 4 | /.pnp 5 | .pnp.js 6 | 7 | # testing 8 | /coverage 9 | 10 | # production 11 | */**/build 12 | */**/.docusaurus 13 | /lib/ 14 | */**/lib 15 | 16 | # misc 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | .idea 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | .env 29 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn pretty-quick --staged 5 | yarn lint-staged 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | */**/node_modules 4 | /.pnp 5 | .pnp.js 6 | 7 | # testing 8 | /coverage 9 | 10 | # misc 11 | .DS_Store 12 | .env.local 13 | .env.development.local 14 | .env.test.local 15 | .env.production.local 16 | 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/CHANGELOG.MD: -------------------------------------------------------------------------------- 1 | # Release notes - Typescript Wallet SDK Key Manager - 1.9.0 2 | * Version bump 3 | 4 | # Release notes - Typescript Wallet SDK Key Manager - 1.8.0 5 | 6 | ### Added 7 | * Upgrades `@stellar/stellar-sdk` to `13.0.0-beta.1` which supports the `Protocol 22` XDR (#172) 8 | 9 | # Release notes - Typescript Wallet SDK Key Manager - 1.7.0 10 | 11 | ### Added 12 | * Support for `home_domain` on `GET /auth` request (#151) 13 | 14 | ### Fixed 15 | * Replace `tweetnacl-util` with `@stablelib` packages (#149) 16 | * ^ this fixes the lib `crashing` on `React Native` environment 17 | 18 | # Release notes - Typescript Wallet SDK Key Manager - 1.6.0 19 | 20 | ### Added 21 | * Upgrade @stellar/stellar-sdk to 12.1.0 (#143) 22 | 23 | # Release notes - Typescript Wallet SDK Key Manager - 1.5.0 24 | 25 | ### Added 26 | * Challenge token to param to auth header 27 | 28 | ### Fixed 29 | * Update BrowserStorageConfigParams method types 30 | 31 | # Release notes - Typescript Wallet SDK Key Manager - 1.4.0 32 | 33 | ### Added 34 | * Init to the project, added key manager functionality 35 | 36 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/README.md: -------------------------------------------------------------------------------- 1 | # Stellar Typescript Wallet Key Manager SDK [![npm version](https://badge.fury.io/js/@stellar%2Ftypescript-wallet-sdk-km.svg)](https://badge.fury.io/js/@stellar%2Ftypescript-wallet-sdk-km) 2 | 3 | The Typescript Wallet Key Manager SDK is a library that allows developers to use 4 | key managing functionality in their wallet applications. It works in conjuction 5 | with the main 6 | [Typescript Wallet SDK](https://github.com/stellar/typescript-wallet-sdk) to 7 | hold all the functionality a developer would need to create a wallet for the 8 | stellar network. 9 | 10 | ## Dependency 11 | 12 | The library is available via npm. To import `typescript-wallet-sdk-km` you need 13 | to add it as a dependency to your code: 14 | 15 | yarn: 16 | 17 | ```shell 18 | yarn add @stellar/typescript-wallet-sdk-km 19 | ``` 20 | 21 | npm: 22 | 23 | ```shell 24 | npm install @stellar/typescript-wallet-sdk-km 25 | ``` 26 | 27 | ## Introduction 28 | 29 | Here's a small example on how to use the KeyManager to store and retrieve a key: 30 | 31 | Import the package: 32 | 33 | ```typescript 34 | import { KeyManager, MemoryKeyStore } from "@stellar/typescript-wallet-sdk-km"; 35 | ``` 36 | 37 | Creating a KeyManager class using simple memory key storage: 38 | 39 | ```typescript 40 | const testStore = new MemoryKeyStore(); 41 | const testKeyManager = new KeyManager({ keyStore: testStore }); 42 | ``` 43 | 44 | Store an encrypted key: 45 | 46 | ```typescript 47 | const id = "this is a my test id"; 48 | testKeyManager.registerEncrypter(IdentityEncrypter); 49 | await testKeyManager.storeKey({ 50 | key: { 51 | id, 52 | type: KeyType.plaintextKey, 53 | publicKey: "TestPublicKey", 54 | privateKey: "TestPrivateKey", 55 | }, 56 | password: "test", 57 | encrypterName: "IdentityEncrypter", 58 | }); 59 | ``` 60 | 61 | Retrieve the stored key: 62 | 63 | ```typescript 64 | const keyData = await testKeyManager.loadKey(id, password); 65 | ``` 66 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/babel.config.js: -------------------------------------------------------------------------------- 1 | const parentConfig = require("../../babel.config"); 2 | 3 | module.exports = { 4 | ...parentConfig, 5 | }; 6 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@stellar/typescript-wallet-sdk-km", 3 | "version": "1.9.0", 4 | "engines": { 5 | "node": ">=18" 6 | }, 7 | "browser": "./lib/bundle_browser.js", 8 | "main": "./lib/bundle.js", 9 | "types": "./lib/index.d.ts", 10 | "license": "Apache-2.0", 11 | "private": false, 12 | "devDependencies": { 13 | "@babel/preset-env": "^7.24.0", 14 | "@babel/preset-typescript": "^7.23.3", 15 | "@stellar/prettier-config": "^1.0.1", 16 | "@stellar/tsconfig": "^1.0.2", 17 | "@types/jest": "^29.5.12", 18 | "@typescript-eslint/parser": "^7.1.1", 19 | "@stellar/typescript-wallet-sdk": "*", 20 | "babel-jest": "^29.7.0", 21 | "husky": "^9.0.11", 22 | "jest": "^29.7.0", 23 | "jest-mock-random": "^1.1.1", 24 | "node-localstorage": "^3.0.5", 25 | "npm-run-all": "^4.1.5", 26 | "sinon": "^17.0.1", 27 | "ts-jest": "^29.1.2", 28 | "ts-loader": "^9.5.1", 29 | "tslib": "^2.6.2", 30 | "typescript": "^5.3.3", 31 | "webpack": "^5.90.3", 32 | "webpack-cli": "^5.1.4" 33 | }, 34 | "dependencies": { 35 | "@albedo-link/intent": "^0.12.0", 36 | "@ledgerhq/hw-app-str": "^6.28.4", 37 | "@ledgerhq/hw-transport-u2f": "^5.36.0-deprecated", 38 | "@stablelib/base64": "^2.0.0", 39 | "@stablelib/utf8": "^2.0.0", 40 | "@stellar/freighter-api": "^2.0.0", 41 | "@stellar/stellar-sdk": "13.0.0-beta.1", 42 | "@trezor/connect-plugin-stellar": "^9.0.2", 43 | "bignumber.js": "^9.1.2", 44 | "scrypt-async": "^2.0.1", 45 | "trezor-connect": "^8.2.12", 46 | "tweetnacl": "^1.0.3" 47 | }, 48 | "scripts": { 49 | "test": "jest --watchAll", 50 | "test:ci": "jest --ci", 51 | "build:web": "webpack --config webpack.config.js", 52 | "build:node": "webpack --env NODE=true --config webpack.config.js", 53 | "build": "run-p build:web build:node" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Handlers/albedo.ts: -------------------------------------------------------------------------------- 1 | import albedo from "@albedo-link/intent"; 2 | import { 3 | Networks, 4 | Transaction, 5 | TransactionBuilder, 6 | } from "@stellar/stellar-sdk"; 7 | 8 | import { 9 | HandlerSignTransactionParams, 10 | KeyTypeHandler, 11 | KeyType, 12 | } from "../Types"; 13 | 14 | export const albedoHandler: KeyTypeHandler = { 15 | keyType: KeyType.albedo, 16 | async signTransaction(params: HandlerSignTransactionParams) { 17 | const { transaction, key } = params; 18 | 19 | if (key.privateKey !== "") { 20 | throw new Error( 21 | `Non-ledger key sent to ledger handler: ${JSON.stringify( 22 | key.publicKey, 23 | )}`, 24 | ); 25 | } 26 | 27 | try { 28 | const xdr = transaction.toXDR(); 29 | const response = await albedo.tx({ xdr }); 30 | 31 | if (!response.signed_envelope_xdr) { 32 | throw new Error("We couldn’t sign the transaction with Albedo."); 33 | } 34 | 35 | // fromXDR() returns type "Transaction | FeeBumpTransaction" and 36 | // signTransaction() doesn't like "| FeeBumpTransaction" type, so casting 37 | // to "Transaction" type. 38 | return TransactionBuilder.fromXDR( 39 | response.signed_envelope_xdr, 40 | Networks.PUBLIC, 41 | ) as Transaction; 42 | } catch (error) { 43 | const errorMsg = error.toString(); 44 | throw new Error( 45 | `We couldn’t sign the transaction with Albedo. ${errorMsg}.`, 46 | ); 47 | } 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Handlers/freighter.ts: -------------------------------------------------------------------------------- 1 | import freighterApi from "@stellar/freighter-api"; 2 | import { 3 | Networks, 4 | Transaction, 5 | TransactionBuilder, 6 | } from "@stellar/stellar-sdk"; 7 | 8 | import { 9 | HandlerSignTransactionParams, 10 | KeyTypeHandler, 11 | KeyType, 12 | } from "../Types"; 13 | 14 | export const freighterHandler: KeyTypeHandler = { 15 | keyType: KeyType.freighter, 16 | async signTransaction(params: HandlerSignTransactionParams) { 17 | const { transaction, key, custom } = params; 18 | 19 | if (key.privateKey !== "") { 20 | throw new Error( 21 | `Non-ledger key sent to ledger handler: ${JSON.stringify( 22 | key.publicKey, 23 | )}`, 24 | ); 25 | } 26 | 27 | try { 28 | const response = await freighterApi.signTransaction( 29 | transaction.toXDR(), 30 | custom && custom.network ? custom.network : undefined, 31 | ); 32 | 33 | // fromXDR() returns type "Transaction | FeeBumpTransaction" and 34 | // signTransaction() doesn't like "| FeeBumpTransaction" type, so casting 35 | // to "Transaction" type. 36 | return TransactionBuilder.fromXDR( 37 | response, 38 | Networks.PUBLIC, 39 | ) as Transaction; 40 | } catch (error) { 41 | const errorMsg = error.toString(); 42 | throw new Error( 43 | `We couldn’t sign the transaction with Freighter. ${errorMsg}.`, 44 | ); 45 | } 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Handlers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./albedo"; 2 | export * from "./freighter"; 3 | export * from "./ledger"; 4 | export * from "./plaintextKey"; 5 | // TODO - fix trezor errors 6 | // export * from "./trezor"; 7 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Handlers/ledger.ts: -------------------------------------------------------------------------------- 1 | import LedgerStr from "@ledgerhq/hw-app-str"; 2 | import LedgerTransport from "@ledgerhq/hw-transport-u2f"; 3 | import { Keypair, xdr } from "@stellar/stellar-sdk"; 4 | 5 | import { 6 | HandlerSignTransactionParams, 7 | KeyTypeHandler, 8 | KeyType, 9 | } from "../Types"; 10 | 11 | export const ledgerHandler: KeyTypeHandler = { 12 | keyType: KeyType.ledger, 13 | async signTransaction(params: HandlerSignTransactionParams) { 14 | const { transaction, key } = params; 15 | 16 | if (key.privateKey !== "") { 17 | throw new Error( 18 | `Non-ledger key sent to ledger handler: ${JSON.stringify( 19 | key.publicKey, 20 | )}`, 21 | ); 22 | } 23 | 24 | /* 25 | There's a naive way to do this (to keep all functions stateless and 26 | make the connection anew each time), and there's some way of weaving state 27 | into this. 28 | 29 | Gonna do the naive thing first and then figure out how to do this right. 30 | */ 31 | const transport = await LedgerTransport.create(60 * 1000); 32 | const ledgerApi = new LedgerStr(transport); 33 | const result = await ledgerApi.signTransaction( 34 | key.path, 35 | transaction.signatureBase(), 36 | ); 37 | 38 | const keyPair = Keypair.fromPublicKey(key.publicKey); 39 | const decoratedSignature = new xdr.DecoratedSignature({ 40 | hint: keyPair.signatureHint(), 41 | signature: result.signature, 42 | }); 43 | transaction.signatures.push(decoratedSignature); 44 | 45 | return Promise.resolve(transaction); 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Handlers/plaintextKey.ts: -------------------------------------------------------------------------------- 1 | import { Keypair } from "@stellar/stellar-sdk"; 2 | 3 | import { 4 | HandlerSignTransactionParams, 5 | KeyTypeHandler, 6 | KeyType, 7 | } from "../Types"; 8 | 9 | export const plaintextKeyHandler: KeyTypeHandler = { 10 | keyType: KeyType.plaintextKey, 11 | signTransaction(params: HandlerSignTransactionParams) { 12 | const { transaction, key } = params; 13 | if (key.privateKey === "") { 14 | throw new Error( 15 | `Non-plaintext key sent to plaintext handler: ${JSON.stringify( 16 | key.publicKey, 17 | )}`, 18 | ); 19 | } 20 | 21 | const keyPair = Keypair.fromSecret(key.privateKey); 22 | 23 | /* 24 | * NOTE: we need to use the combo of getKeypairSignature() + addSignature() 25 | * here in place of the shorter sign() call because sign() results in a 26 | * "XDR Write Error: [object Object] is not a DecoratedSignature" error 27 | * on React Native whenever we try to call transaction.toXDR() on the signed 28 | * transaction. 29 | */ 30 | 31 | const signature = transaction.getKeypairSignature(keyPair); 32 | transaction.addSignature(keyPair.publicKey(), signature); 33 | 34 | return Promise.resolve(transaction); 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Handlers/trezor.ts: -------------------------------------------------------------------------------- 1 | import TrezorConnect from "trezor-connect"; 2 | import transformTransaction from "@trezor/connect-plugin-stellar"; 3 | 4 | import { 5 | HandlerSignTransactionParams, 6 | KeyTypeHandler, 7 | KeyType, 8 | } from "../Types"; 9 | 10 | export const trezorHandler: KeyTypeHandler = { 11 | keyType: KeyType.trezor, 12 | async signTransaction(params: HandlerSignTransactionParams) { 13 | const { transaction, key, custom } = params; 14 | 15 | if (key.privateKey !== "") { 16 | throw new Error( 17 | `Non-ledger key sent to ledger handler: ${JSON.stringify( 18 | key.publicKey, 19 | )}`, 20 | ); 21 | } 22 | 23 | if (!custom || !custom.email || !custom.appUrl) { 24 | throw new Error( 25 | `Trezor Connect manifest with "email" and "appUrl" props is required. 26 | Make sure they are passed through "custom" prop.`, 27 | ); 28 | } 29 | 30 | try { 31 | TrezorConnect.manifest({ 32 | email: custom.email, 33 | appUrl: custom.appUrl, 34 | }); 35 | 36 | const trezorParams = transformTransaction("m/44'/148'/0'", transaction); 37 | const response = await TrezorConnect.stellarSignTransaction(trezorParams); 38 | 39 | if (response.success) { 40 | const signature = Buffer.from( 41 | response.payload.signature, 42 | "hex", 43 | ).toString("base64"); 44 | transaction.addSignature(key.publicKey, signature); 45 | 46 | return transaction; 47 | } else { 48 | throw new Error( 49 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 50 | (response.payload as any).error || 51 | "We couldn’t sign the transaction with Trezor.", 52 | ); 53 | } 54 | } catch (error) { 55 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 56 | const errorMsg = error.toString(); 57 | throw new Error( 58 | `We couldn’t sign the transaction with Trezor. ${errorMsg}.`, 59 | ); 60 | } 61 | }, 62 | }; 63 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Helpers/getKeyMetadata.ts: -------------------------------------------------------------------------------- 1 | import { EncryptedKey, KeyMetadata } from "../Types"; 2 | 3 | export function getKeyMetadata(encryptedKey: EncryptedKey): KeyMetadata { 4 | const { id } = encryptedKey; 5 | 6 | return { 7 | id, 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./getKeyMetadata"; 2 | export { decrypt, encrypt, NONCE_BYTES } from "./scryptEncryption"; 3 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Helpers/scryptEncryption.ts: -------------------------------------------------------------------------------- 1 | import { encode as utf8Encode, decode as utf8Decode } from "@stablelib/utf8"; 2 | import { 3 | encode as base64Encode, 4 | decode as base64Decode, 5 | } from "@stablelib/base64"; 6 | import scrypt from "scrypt-async"; 7 | import nacl from "tweetnacl"; 8 | 9 | export interface ScryptPassParams { 10 | password: string; 11 | salt: string; 12 | dkLen?: number; 13 | } 14 | 15 | export interface EncryptParams { 16 | phrase: string; 17 | password: string; 18 | 19 | // these should only be used for testing! 20 | salt?: string; 21 | nonce?: Uint8Array; 22 | } 23 | 24 | export interface EncryptResponse { 25 | encryptedPhrase: string; 26 | salt: string; 27 | } 28 | 29 | export interface DecryptParams { 30 | phrase: string; 31 | password: string; 32 | salt: string; 33 | } 34 | 35 | export const RECOVERY_CODE_NBITS = 160; 36 | export const RECOVERY_CODE_NWORDS = (RECOVERY_CODE_NBITS / 32) * 3; 37 | export const SALT_BYTES = 32; 38 | export const NONCE_BYTES = nacl.secretbox.nonceLength; // 24 bytes 39 | export const LOCAL_KEY_BYTES = nacl.secretbox.keyLength; // 32 bytes 40 | export const CRYPTO_V1 = 1; 41 | export const CURRENT_CRYPTO_VERSION = CRYPTO_V1; 42 | export const KEY_LEN = nacl.secretbox.keyLength; // 32 bytes 43 | 44 | /** 45 | * Convert password from user into a derived key for encryption 46 | * @param {ScryptPassParams} params - The scrypt pass params. 47 | * @param {string} params.password plaintext password from user 48 | * @param {string} params.salt salt (should be randomly generated) 49 | * @param {number} params.dkLen length of the derived key to output 50 | * @returns {Uint8Array} bytes of the derived key 51 | */ 52 | function scryptPass(params: ScryptPassParams): Promise { 53 | const { password, salt, dkLen = KEY_LEN } = params; 54 | const [N, r, p] = [32768, 8, 1]; 55 | return new Promise((resolve, reject) => { 56 | scrypt( 57 | password, 58 | salt, 59 | { N, r, p, dkLen, encoding: "binary" }, 60 | (derivedKey: Uint8Array) => { 61 | if (derivedKey) { 62 | resolve(derivedKey); 63 | } else { 64 | reject(new Error("scryptPass failed, derivedKey is null")); 65 | } 66 | }, 67 | ); 68 | }); 69 | } 70 | 71 | function generateSalt(): string { 72 | return base64Encode(nacl.randomBytes(SALT_BYTES)); 73 | } 74 | 75 | /** 76 | * Encrypt a phrase using scrypt. 77 | * @async 78 | * @param {object} params Params object 79 | * @param {string} params.phrase Phrase to be encrypted 80 | * @param {string} params.password A password to encrypt the string with. 81 | * @param {string} [params.salt] A static salt. Use only for unit tests. 82 | * @param {string} [params.nonce] A static nonce. Use only for unit tests. 83 | * @returns {Promise} the encrypt response 84 | */ 85 | export async function encrypt(params: EncryptParams): Promise { 86 | const { phrase, password, salt, nonce } = params; 87 | const secretboxSalt = salt || generateSalt(); 88 | 89 | const secretboxNonce = nonce || nacl.randomBytes(NONCE_BYTES); 90 | const scryptedPass = await scryptPass({ password, salt: secretboxSalt }); 91 | const textBytes = utf8Encode(phrase); 92 | const cipherText = nacl.secretbox(textBytes, secretboxNonce, scryptedPass); 93 | 94 | if (!cipherText) { 95 | throw new Error("Encryption failed"); 96 | } 97 | 98 | // merge these into one array 99 | // (in a somewhat ugly way, since TS doesn't like destructuring Uint8Arrays) 100 | const bundle = new Uint8Array(1 + secretboxNonce.length + cipherText.length); 101 | bundle.set([CURRENT_CRYPTO_VERSION]); 102 | bundle.set(secretboxNonce, 1); 103 | bundle.set(cipherText, 1 + secretboxNonce.length); 104 | 105 | return { 106 | encryptedPhrase: base64Encode(bundle), 107 | salt: secretboxSalt, 108 | }; 109 | } 110 | 111 | export async function decrypt(params: DecryptParams): Promise { 112 | const { phrase, password, salt } = params; 113 | const scryptedPass = await scryptPass({ password, salt }); 114 | 115 | const bundle = base64Decode(phrase); 116 | const version = bundle[0]; 117 | let decryptedBytes; 118 | if (version === CRYPTO_V1) { 119 | const nonce = bundle.slice(1, 1 + NONCE_BYTES); 120 | const cipherText = bundle.slice(1 + NONCE_BYTES); 121 | decryptedBytes = nacl.secretbox.open(cipherText, nonce, scryptedPass); 122 | } else { 123 | throw new Error(`Cipher version ${version} not supported.`); 124 | } 125 | if (!decryptedBytes) { 126 | throw new Error("That passphrase wasn’t valid."); 127 | } 128 | return utf8Decode(decryptedBytes); 129 | } 130 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Plugins/BrowserStorageFacade.ts: -------------------------------------------------------------------------------- 1 | import { EncryptedKey } from "../Types"; 2 | 3 | export interface BrowserStorageConfigParams { 4 | prefix?: string; 5 | storage: { 6 | get: ( 7 | key?: null | string | string[] | Record, 8 | ) => Promise>; 9 | remove: (key: string | string[]) => Promise; 10 | set: (items: Record) => Promise; 11 | }; 12 | } 13 | 14 | const PREFIX = "stellarkeys"; 15 | 16 | /** 17 | * Facade for `BrowserStorageKeyStore` encapsulating the access to the actual 18 | * browser storage 19 | */ 20 | export class BrowserStorageFacade { 21 | private storage: Storage | null; 22 | private prefix: string; 23 | 24 | constructor() { 25 | this.storage = null; 26 | this.prefix = PREFIX; 27 | } 28 | 29 | public configure(params: BrowserStorageConfigParams) { 30 | Object.assign(this, params); 31 | } 32 | 33 | public async hasKey(id: string) { 34 | this.check(); 35 | 36 | return this.storage !== null 37 | ? !!Object.keys(await this.storage.get(`${this.prefix}:${id}`)).length 38 | : null; 39 | } 40 | 41 | public async getKey(id: string) { 42 | this.check(); 43 | const key = `${this.prefix}:${id}`; 44 | const itemObj = this.storage !== null ? await this.storage.get(key) : null; 45 | 46 | const item = itemObj[key]; 47 | return item || null; 48 | } 49 | 50 | public setKey(id: string, key: EncryptedKey) { 51 | this.check(); 52 | return this.storage !== null 53 | ? this.storage.set({ [`${this.prefix}:${id}`]: { ...key } }) 54 | : null; 55 | } 56 | 57 | public removeKey(id: string) { 58 | this.check(); 59 | return this.storage !== null 60 | ? this.storage.remove(`${this.prefix}:${id}`) 61 | : null; 62 | } 63 | 64 | public async getAllKeys() { 65 | this.check(); 66 | const regexp = RegExp(`^${PREFIX}\\:(.*)`); 67 | const keys: EncryptedKey[] = []; 68 | 69 | if (this.storage !== null) { 70 | const storageObj = await this.storage.get(null); 71 | const storageKeys = Object.keys(storageObj); 72 | for (const storageKey of storageKeys) { 73 | const raw_id = storageKey; 74 | if (raw_id !== null && regexp.test(raw_id)) { 75 | const key = await this.getKey(regexp.exec(raw_id)![1]); 76 | if (key !== null) { 77 | keys.push(key); 78 | } 79 | } 80 | } 81 | } 82 | return keys; 83 | } 84 | 85 | private check() { 86 | if (this.storage === null) { 87 | throw new Error("A storage object must have been set"); 88 | } 89 | if (this.prefix === "") { 90 | throw new Error("A non-empty prefix must have been set"); 91 | } 92 | return this.storage !== null && this.prefix !== ""; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Plugins/BrowserStorageKeyStore.ts: -------------------------------------------------------------------------------- 1 | import { getKeyMetadata } from "../Helpers/getKeyMetadata"; 2 | import { EncryptedKey, KeyMetadata, KeyStore } from "../Types"; 3 | import { 4 | BrowserStorageConfigParams, 5 | BrowserStorageFacade, 6 | } from "./BrowserStorageFacade"; 7 | 8 | /** 9 | * KeyStore for Chrome and Firefox browser storage API: 10 | * https://developer.chrome.com/docs/extensions/reference/storage/. 11 | * Once instantiated and configured, pass it to the `KeyManager` contructor to 12 | * handle the storage of encrypted keys. 13 | * ```js 14 | * const browserKeyStore = new KeyManagerPlugins.BrowserStorageKeyStore(); 15 | * browserKeyStore.configure({ storage: chrome.storage.local }); 16 | * const keyManager = new KeyManager({ 17 | * keyStore: browserKeyStore 18 | * }); 19 | * ``` 20 | */ 21 | export class BrowserStorageKeyStore implements KeyStore { 22 | public name: string; 23 | private keyStore: BrowserStorageFacade; 24 | 25 | constructor() { 26 | this.name = "BrowserStorageKeyStore"; 27 | this.keyStore = new BrowserStorageFacade(); 28 | } 29 | 30 | /** 31 | * The configuration is where the storage engine is set up and configured. 32 | * It must follow the Storage interface : 33 | * https://developer.chrome.com/docs/extensions/reference/storage/). 34 | * This is mostly for use with Chrome and Firefox storage in addition to 35 | * libraries that shim this (for ex: webextension-polyfill) 36 | * @param {BrowserStorageConfigParams} params A configuration object. 37 | * @param {Storage} params.storage The Storage instance. Required. 38 | * @param {string} [params.prefix] The prefix for the names in the storage. 39 | * @returns {Promise} resolved data 40 | */ 41 | public configure(params: BrowserStorageConfigParams) { 42 | try { 43 | this.keyStore.configure(params); 44 | return Promise.resolve(); 45 | } catch (e) { 46 | return Promise.reject(e); 47 | } 48 | } 49 | 50 | public async storeKeys(keys: EncryptedKey[]) { 51 | // We can't store keys if they're already there 52 | const usedKeys: EncryptedKey[] = []; 53 | 54 | for (const encryptedKey of keys) { 55 | const hasKey = await this.keyStore.hasKey(encryptedKey.id); 56 | if (hasKey) { 57 | usedKeys.push(encryptedKey); 58 | } 59 | } 60 | 61 | if (usedKeys.length) { 62 | return Promise.reject( 63 | `Some keys were already stored in the keystore: ${usedKeys 64 | .map((k) => k.id) 65 | .join(", ")}`, 66 | ); 67 | } 68 | 69 | const keysMetadata: KeyMetadata[] = []; 70 | 71 | for (const encryptedKey of keys) { 72 | this.keyStore.setKey(encryptedKey.id, encryptedKey); 73 | keysMetadata.push(getKeyMetadata(encryptedKey)); 74 | } 75 | 76 | return Promise.resolve(keysMetadata); 77 | } 78 | 79 | public updateKeys(keys: EncryptedKey[]) { 80 | // we can't update keys if they're already stored 81 | const invalidKeys: EncryptedKey[] = keys.filter( 82 | async (encryptedKey: EncryptedKey) => 83 | !(await this.keyStore.hasKey(encryptedKey.id)), 84 | ); 85 | 86 | if (invalidKeys.length) { 87 | return Promise.reject( 88 | `Some keys couldn't be found in the keystore: ${invalidKeys 89 | .map((k) => k.id) 90 | .join(", ")}`, 91 | ); 92 | } 93 | 94 | const keysMetadata = keys.map((encryptedKey: EncryptedKey) => { 95 | this.keyStore.setKey(encryptedKey.id, encryptedKey); 96 | return getKeyMetadata(encryptedKey); 97 | }); 98 | 99 | return Promise.resolve(keysMetadata); 100 | } 101 | 102 | public async loadKey(id: string) { 103 | const key = await this.keyStore.getKey(id); 104 | if (!key) { 105 | return Promise.reject(id); 106 | } 107 | return Promise.resolve(key); 108 | } 109 | 110 | public async removeKey(id: string) { 111 | if (!this.keyStore.hasKey(id)) { 112 | return Promise.reject(id); 113 | } 114 | 115 | const key = await this.keyStore.getKey(id); 116 | const metadata: KeyMetadata = getKeyMetadata(key); 117 | this.keyStore.removeKey(id); 118 | 119 | return Promise.resolve(metadata); 120 | } 121 | 122 | public async loadAllKeys() { 123 | const keys = await this.keyStore.getAllKeys(); 124 | return Promise.resolve(keys); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Plugins/IdentityEncrypter.ts: -------------------------------------------------------------------------------- 1 | import { DecryptParams, Encrypter, EncryptParams } from "../Types"; 2 | 3 | const NAME = "IdentityEncrypter"; 4 | 5 | /** 6 | * "Encrypt" keys in a very basic, naive way. 7 | */ 8 | export const IdentityEncrypter: Encrypter = { 9 | name: NAME, 10 | encryptKey(params: EncryptParams) { 11 | const { key } = params; 12 | const { type, privateKey, publicKey, path, extra, ...props } = key; 13 | 14 | return Promise.resolve({ 15 | ...props, 16 | encryptedBlob: JSON.stringify({ 17 | type, 18 | publicKey, 19 | privateKey, 20 | path, 21 | extra, 22 | }), 23 | encrypterName: NAME, 24 | salt: "identity", 25 | }); 26 | }, 27 | 28 | decryptKey(params: DecryptParams) { 29 | const { encryptedKey } = params; 30 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 31 | const { encrypterName, salt, encryptedBlob, ...props } = 32 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 33 | encryptedKey as any; 34 | 35 | const data = JSON.parse(encryptedBlob); 36 | 37 | return Promise.resolve({ ...props, ...data }); 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Plugins/LocalStorageFacade.ts: -------------------------------------------------------------------------------- 1 | import { EncryptedKey } from "../Types"; 2 | 3 | export interface LocalStorageConfigParams { 4 | prefix?: string; 5 | storage: Storage; 6 | } 7 | 8 | /** 9 | * Facade for `LocalStorageKeyStore` encapsulating the access to the actual 10 | * local storage 11 | */ 12 | export class LocalStorageFacade { 13 | private storage: Storage | null; 14 | private prefix: string; 15 | 16 | constructor() { 17 | this.storage = null; 18 | this.prefix = "stellarkeys"; 19 | } 20 | 21 | public configure(params: LocalStorageConfigParams) { 22 | Object.assign(this, params); 23 | } 24 | 25 | public hasKey(id: string) { 26 | this.check(); 27 | return this.storage !== null 28 | ? this.storage.getItem(`${this.prefix}:${id}`) !== null 29 | : null; 30 | } 31 | 32 | public getKey(id: string) { 33 | this.check(); 34 | const item = 35 | this.storage !== null 36 | ? this.storage.getItem(`${this.prefix}:${id}`) 37 | : null; 38 | return item ? JSON.parse(item) : null; 39 | } 40 | 41 | public setKey(id: string, key: EncryptedKey) { 42 | this.check(); 43 | return this.storage !== null 44 | ? this.storage.setItem(`${this.prefix}:${id}`, JSON.stringify({ ...key })) 45 | : null; 46 | } 47 | 48 | public removeKey(id: string) { 49 | this.check(); 50 | return this.storage !== null 51 | ? this.storage.removeItem(`${this.prefix}:${id}`) 52 | : null; 53 | } 54 | 55 | public getAllKeys() { 56 | this.check(); 57 | const regexp = RegExp(`^${this.prefix}\\:(.*)`); 58 | const keys: EncryptedKey[] = []; 59 | 60 | if (this.storage !== null) { 61 | for (let i = 0; i < this.storage.length; i++) { 62 | const raw_id = this.storage.key(i); 63 | if (raw_id !== null && regexp.test(raw_id)) { 64 | const key = this.getKey(regexp.exec(raw_id)![1]); 65 | if (key !== null) { 66 | keys.push(key); 67 | } 68 | } 69 | } 70 | } 71 | return keys; 72 | } 73 | 74 | private check() { 75 | if (this.storage === null) { 76 | throw new Error("A storage object must have been set"); 77 | } 78 | if (this.prefix === "") { 79 | throw new Error("A non-empty prefix must have been set"); 80 | } 81 | return this.storage !== null && this.prefix !== ""; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Plugins/LocalStorageKeyStore.ts: -------------------------------------------------------------------------------- 1 | import { getKeyMetadata } from "../Helpers/getKeyMetadata"; 2 | import { EncryptedKey, KeyMetadata, KeyStore } from "../Types"; 3 | import { 4 | LocalStorageConfigParams, 5 | LocalStorageFacade, 6 | } from "./LocalStorageFacade"; 7 | 8 | /** 9 | * KeyStore for the Web Storage API : 10 | * https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API. 11 | * Once instantiated and configured, pass it to the `KeyManager` contructor to 12 | * handle the storage of encrypted keys. 13 | * ```js 14 | * const localKeyStore = new KeyManagerPlugins.LocalStorageKeyStore(); 15 | * localKeyStore.configure({ storage: localStorage }); 16 | * const keyManager = new KeyManager({ 17 | * keyStore: localKeyStore 18 | * }); 19 | * ``` 20 | */ 21 | export class LocalStorageKeyStore implements KeyStore { 22 | public name: string; 23 | private keyStore: LocalStorageFacade; 24 | 25 | constructor() { 26 | this.name = "LocalStorageKeyStore"; 27 | this.keyStore = new LocalStorageFacade(); 28 | } 29 | 30 | /** 31 | * The configuration is where the storage engine is set up and configured. 32 | * It must follow the Storage interface : 33 | * https://developer.mozilla.org/en-US/docs/Web/API/Storage). 34 | * In the DOM environment it can be `localStorage` or `sessionStorage`. 35 | * In a Node environment, there are some substitution libraries available, 36 | * *node-localstorage* for instance. 37 | * If not set, the calls to the other methods will fail. 38 | * @param {LocalStorageConfigParams} params A configuration object. 39 | * @param {Storage} params.storage The Storage instance. Required. 40 | * @param {string} [params.prefix] The prefix for the names in the storage. 41 | * @returns {Promise} resolved data 42 | */ 43 | public configure(params: LocalStorageConfigParams) { 44 | try { 45 | this.keyStore.configure(params); 46 | return Promise.resolve(); 47 | } catch (e) { 48 | return Promise.reject(e); 49 | } 50 | } 51 | 52 | public storeKeys(keys: EncryptedKey[]) { 53 | // We can't store keys if they're already there 54 | const invalidKeys: EncryptedKey[] = keys.filter( 55 | (encryptedKey: EncryptedKey) => this.keyStore.hasKey(encryptedKey.id), 56 | ); 57 | 58 | if (invalidKeys.length) { 59 | return Promise.reject( 60 | `Some keys were already stored in the keystore: ${invalidKeys 61 | .map((k) => k.id) 62 | .join(", ")}`, 63 | ); 64 | } 65 | 66 | const keysMetadata = keys.map((encryptedKey: EncryptedKey) => { 67 | this.keyStore.setKey(encryptedKey.id, encryptedKey); 68 | return getKeyMetadata(encryptedKey); 69 | }); 70 | 71 | return Promise.resolve(keysMetadata); 72 | } 73 | 74 | public updateKeys(keys: EncryptedKey[]) { 75 | // we can't update keys if they're already stored 76 | const invalidKeys: EncryptedKey[] = keys.filter( 77 | (encryptedKey: EncryptedKey) => !this.keyStore.hasKey(encryptedKey.id), 78 | ); 79 | 80 | if (invalidKeys.length) { 81 | return Promise.reject( 82 | `Some keys couldn't be found in the keystore: ${invalidKeys 83 | .map((k) => k.id) 84 | .join(", ")}`, 85 | ); 86 | } 87 | 88 | const keysMetadata = keys.map((encryptedKey: EncryptedKey) => { 89 | this.keyStore.setKey(encryptedKey.id, encryptedKey); 90 | return getKeyMetadata(encryptedKey); 91 | }); 92 | 93 | return Promise.resolve(keysMetadata); 94 | } 95 | 96 | public loadKey(id: string) { 97 | const key = this.keyStore.getKey(id); 98 | if (!key) { 99 | return Promise.reject(id); 100 | } 101 | return Promise.resolve(key); 102 | } 103 | 104 | public removeKey(id: string) { 105 | if (!this.keyStore.hasKey(id)) { 106 | return Promise.reject(id); 107 | } 108 | 109 | const metadata: KeyMetadata = getKeyMetadata(this.keyStore.getKey(id)); 110 | this.keyStore.removeKey(id); 111 | 112 | return Promise.resolve(metadata); 113 | } 114 | 115 | public loadAllKeys() { 116 | return Promise.resolve(this.keyStore.getAllKeys()); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Plugins/MemoryKeyStore.ts: -------------------------------------------------------------------------------- 1 | import { EncryptedKey, KeyMetadata, KeyStore } from "../Types"; 2 | import { getKeyMetadata } from "../Helpers/getKeyMetadata"; 3 | 4 | interface MemoryStorer { 5 | [id: string]: EncryptedKey; 6 | } 7 | 8 | export class MemoryKeyStore implements KeyStore { 9 | public name: string; 10 | private keyStore: MemoryStorer; 11 | 12 | constructor() { 13 | this.name = "MemoryKeyStore"; 14 | this.keyStore = {}; 15 | } 16 | 17 | public configure() { 18 | return Promise.resolve(); 19 | } 20 | 21 | public storeKeys(keys: EncryptedKey[]) { 22 | // We can't store keys if they're already there 23 | const invalidKeys: EncryptedKey[] = keys.filter( 24 | (encryptedKey: EncryptedKey) => !!this.keyStore[encryptedKey.id], 25 | ); 26 | 27 | if (invalidKeys.length) { 28 | return Promise.reject( 29 | `Some keys were already stored in the keystore: ${invalidKeys 30 | .map((k) => k.id) 31 | .join(", ")}`, 32 | ); 33 | } 34 | 35 | const keysMetadata = keys.map((encryptedKey: EncryptedKey) => { 36 | this.keyStore[encryptedKey.id] = { 37 | ...encryptedKey, 38 | }; 39 | 40 | return getKeyMetadata(this.keyStore[encryptedKey.id]); 41 | }); 42 | 43 | return Promise.resolve(keysMetadata); 44 | } 45 | 46 | public updateKeys(keys: EncryptedKey[]) { 47 | // we can't update keys if they're already stored 48 | const invalidKeys: EncryptedKey[] = keys.filter( 49 | (encryptedKey: EncryptedKey) => !this.keyStore[encryptedKey.id], 50 | ); 51 | 52 | if (invalidKeys.length) { 53 | return Promise.reject( 54 | `Some keys couldn't be found in the keystore: ${invalidKeys 55 | .map((k) => k.id) 56 | .join(", ")}`, 57 | ); 58 | } 59 | 60 | const keysMetadata = keys.map((encryptedKey: EncryptedKey) => { 61 | const id = encryptedKey.id; 62 | 63 | this.keyStore[id] = { 64 | ...encryptedKey, 65 | }; 66 | 67 | return getKeyMetadata(this.keyStore[id]); 68 | }); 69 | 70 | return Promise.resolve(keysMetadata); 71 | } 72 | 73 | public loadKey(id: string) { 74 | return Promise.resolve(this.keyStore[id]); 75 | } 76 | 77 | public removeKey(id: string) { 78 | if (!this.keyStore[id]) { 79 | return Promise.reject(id); 80 | } 81 | 82 | const metadata: KeyMetadata = getKeyMetadata(this.keyStore[id]); 83 | delete this.keyStore[id]; 84 | 85 | return Promise.resolve(metadata); 86 | } 87 | 88 | public loadAllKeys() { 89 | return Promise.resolve( 90 | Object.values(this.keyStore).map((item: EncryptedKey) => item), 91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Plugins/ScryptEncrypter.ts: -------------------------------------------------------------------------------- 1 | import { decrypt, encrypt } from "../Helpers/scryptEncryption"; 2 | import { 3 | DecryptParams, 4 | EncryptedKey, 5 | Encrypter, 6 | EncryptParams, 7 | } from "../Types"; 8 | 9 | const NAME = "ScryptEncrypter"; 10 | 11 | /** 12 | * Encrypt keys with scrypt, as they are on StellarX.com. 13 | */ 14 | export const ScryptEncrypter: Encrypter = { 15 | name: NAME, 16 | async encryptKey(params: EncryptParams): Promise { 17 | const { key, password } = params; 18 | const { privateKey, path, extra, publicKey, type, ...props } = key; 19 | 20 | const { encryptedPhrase, salt } = await encrypt({ 21 | password, 22 | phrase: JSON.stringify({ privateKey, path, extra, publicKey, type }), 23 | }); 24 | 25 | return { 26 | ...props, 27 | encryptedBlob: encryptedPhrase, 28 | encrypterName: NAME, 29 | salt, 30 | }; 31 | }, 32 | 33 | async decryptKey(params: DecryptParams) { 34 | const { encryptedKey, password } = params; 35 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 36 | const { encrypterName, salt, encryptedBlob, ...props } = encryptedKey; 37 | 38 | const data = JSON.parse( 39 | await decrypt({ phrase: encryptedBlob, salt, password }), 40 | ); 41 | 42 | return { 43 | ...props, 44 | ...data, 45 | }; 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/Plugins/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./IdentityEncrypter"; 2 | export * from "./MemoryKeyStore"; 3 | export * from "./ScryptEncrypter"; 4 | export * from "./BrowserStorageKeyStore"; 5 | export * from "./LocalStorageKeyStore"; 6 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./keyManager"; 2 | export * from "./Handlers"; 3 | export * from "./Plugins"; 4 | export * from "./Helpers"; 5 | export * from "./Types"; 6 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/test/fixtures/keys.ts: -------------------------------------------------------------------------------- 1 | import * as StellarSdk from "@stellar/stellar-sdk"; 2 | 3 | import { EncryptedKey, Key, KeyMetadata, KeyType } from "../../src/Types"; 4 | 5 | export function generatePlaintextKey(): Key { 6 | const account = StellarSdk.Keypair.random(); 7 | const publicKey = account.publicKey(); 8 | const privateKey = account.secret(); 9 | 10 | return { 11 | id: `${Math.random()}`, 12 | type: KeyType.plaintextKey, 13 | publicKey, 14 | privateKey, 15 | }; 16 | } 17 | 18 | export function generateLedgerKey(): Key { 19 | const account = StellarSdk.Keypair.random(); 20 | const publicKey = account.publicKey(); 21 | 22 | return { 23 | id: `${Math.random()}`, 24 | type: KeyType.ledger, 25 | publicKey, 26 | privateKey: "", 27 | path: "44'/148'/0'", 28 | }; 29 | } 30 | 31 | export function generateEncryptedKey(encrypterName: string): EncryptedKey { 32 | const { privateKey, ...key } = generatePlaintextKey(); 33 | 34 | return { 35 | ...key, 36 | encrypterName, 37 | salt: "", 38 | encryptedBlob: `${privateKey}password`, 39 | }; 40 | } 41 | 42 | export function generateKeyMetadata(encrypterName: string): KeyMetadata { 43 | const { id } = generateEncryptedKey(encrypterName); 44 | 45 | return { 46 | id, 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/test/helpers.test.ts: -------------------------------------------------------------------------------- 1 | import { EncryptedKey } from "../src/Types"; 2 | import { getKeyMetadata, decrypt, encrypt, NONCE_BYTES } from "../src/Helpers"; 3 | 4 | describe("scryptEncryption", () => { 5 | test("encrypts and decrypts a key", async () => { 6 | const privateKey = "ARCHANGEL"; 7 | 8 | const password = "This is a really cool password and is good"; 9 | const salt = "Also this salt is really key, and good"; 10 | const nonce = new Uint8Array(NONCE_BYTES).fill(42); 11 | 12 | const { encryptedPhrase } = await encrypt({ 13 | phrase: privateKey, 14 | password, 15 | salt, 16 | nonce, 17 | }); 18 | 19 | expect(encryptedPhrase).toBeTruthy(); 20 | expect(encryptedPhrase).not.toEqual(privateKey); 21 | expect(encryptedPhrase).toEqual( 22 | "ASoqKioqKioqKioqKioqKioqKioqKioqKghXdTQ4aKmd0WIKwT5YjOCtN95jMeXe1UI=", 23 | ); 24 | 25 | const decryptedPhrase = await decrypt({ 26 | phrase: encryptedPhrase, 27 | password, 28 | salt, 29 | }); 30 | 31 | expect(decryptedPhrase).not.toEqual(encryptedPhrase); 32 | expect(decryptedPhrase).toEqual(privateKey); 33 | }); 34 | 35 | test("encrypts and decrypts a StellarX seed", async () => { 36 | const seed = "SCHKKYK3B3MPQKDTUVS37WSVHJ7EY6YRAGKOMOZJUOOMKVXTHTHBPZVL"; 37 | const password = "hello"; 38 | const salt = "salty"; 39 | const nonce = new Uint8Array(NONCE_BYTES).fill(42); 40 | 41 | const { encryptedPhrase } = await encrypt({ 42 | phrase: seed, 43 | password, 44 | salt, 45 | nonce, 46 | }); 47 | 48 | expect(encryptedPhrase).not.toEqual(seed); 49 | expect(encryptedPhrase).toEqual( 50 | "ASoqKioqKioqKioqKioqKioqKioqKioqKgjCVz5H3mKHykeuO6GA8KKJSQrTu9D9Gt8nhO" + 51 | "R7u3iccJc+jV768SEGOtWnwU6x4o46LxhKI8nQGMahV4JpqruESNW8vwt0OQ==", 52 | ); 53 | 54 | const decryptedPhrase = await decrypt({ 55 | phrase: encryptedPhrase, 56 | password, 57 | salt, 58 | }); 59 | 60 | expect(decryptedPhrase).not.toEqual(encryptedPhrase); 61 | expect(decryptedPhrase).toEqual(seed); 62 | }); 63 | }); 64 | 65 | describe("getKeyMetadata", () => { 66 | test("ledger key", () => { 67 | const encryptedKey: EncryptedKey = { 68 | id: "PURIFIER", 69 | encryptedBlob: "BLOB", 70 | encrypterName: "Test", 71 | salt: "SLFKJSDLKFJLSKDJFLKSJD", 72 | }; 73 | 74 | expect(getKeyMetadata(encryptedKey)).toEqual({ 75 | id: "PURIFIER", 76 | }); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "baseUrl": "./", 5 | "outDir": "lib", 6 | "declaration": true, 7 | "declarationDir": "lib" 8 | }, 9 | "include": ["./"] 10 | } 11 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "baseUrl": "src/", 5 | "outDir": "lib", 6 | "declaration": true, 7 | "declarationDir": "lib" 8 | }, 9 | "include": ["src"] 10 | } 11 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-km/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | 4 | module.exports = (env = { NODE: false }) => { 5 | const isBrowser = !env.NODE; 6 | 7 | return { 8 | mode: "development", 9 | entry: "./src/index.ts", 10 | devtool: "source-map", 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.ts$/, 15 | use: "ts-loader", 16 | exclude: /node_modules/, 17 | }, 18 | ], 19 | }, 20 | resolve: { 21 | extensions: [".js", ".json", ".ts"], 22 | fallback: isBrowser 23 | ? { 24 | crypto: require.resolve("crypto-browserify"), 25 | http: require.resolve("stream-http"), 26 | https: require.resolve("https-browserify"), 27 | stream: require.resolve("stream-browserify"), 28 | url: require.resolve("url"), 29 | util: require.resolve("util"), 30 | vm: require.resolve("vm-browserify"), 31 | "process/browser": require.resolve("process/browser"), 32 | } 33 | : {}, 34 | }, 35 | output: { 36 | library: "WalletSDK", 37 | libraryTarget: "umd", 38 | globalObject: "this", 39 | filename: `bundle${isBrowser ? "_browser" : ""}.js`, 40 | path: path.resolve(__dirname, "lib"), 41 | }, 42 | target: isBrowser ? "web" : "node", 43 | plugins: isBrowser 44 | ? [ 45 | new webpack.ProvidePlugin({ 46 | process: "process/browser", 47 | }), 48 | ] 49 | : [], 50 | }; 51 | }; 52 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/CHANGELOG.MD: -------------------------------------------------------------------------------- 1 | # Release notes - Typescript Wallet SDK Soroban - 1.9.0 2 | * Version bump 3 | 4 | # Release notes - Typescript Wallet SDK Soroban - 1.8.0 5 | 6 | ### Added 7 | * Upgrades `@stellar/stellar-sdk` to `13.0.0-beta.1` which supports the `Protocol 22` XDR (#172) 8 | 9 | # Release notes - Typescript Wallet SDK Soroban - 1.7.0 10 | 11 | ### Added 12 | * Bump version to 1.7.0 13 | 14 | # Release notes - Typescript Wallet SDK Soroban - 1.6.0 15 | 16 | ### Added 17 | * Upgrade @stellar/stellar-sdk to 12.1.0 (#143) 18 | 19 | # Release notes - Typescript Wallet SDK Soroban - 1.5.0 20 | 21 | ### Added 22 | * Init to the project, added soroban functionality 23 | * getTokenInvocationArgs function 24 | * Token parsing/formatting functions 25 | * scValyByType function 26 | * generic getInvocationDetails helper 27 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/README.md: -------------------------------------------------------------------------------- 1 | # Stellar Typescript Wallet Soroban SDK [![npm version](https://badge.fury.io/js/@stellar%2Ftypescript-wallet-sdk-soroban.svg)](https://badge.fury.io/js/@stellar%2Ftypescript-wallet-sdk-soroban) 2 | 3 | The Typescript Wallet Soroban SDK is a work-in-progress library that (currently) 4 | allows developers to use soroban helpers in their wallet applications. It works 5 | in conjuction with the main 6 | [Typescript Wallet SDK](https://github.com/stellar/typescript-wallet-sdk) to 7 | hold all the functionality a developer would need to create a wallet for the 8 | stellar network. 9 | 10 | ## Dependency 11 | 12 | The library is available via npm. To import `typescript-wallet-sdk-soroban` you 13 | need to add it as a dependency to your code: 14 | 15 | yarn: 16 | 17 | ```shell 18 | yarn add @stellar/typescript-wallet-sdk-soroban 19 | ``` 20 | 21 | npm: 22 | 23 | ```shell 24 | npm install @stellar/typescript-wallet-sdk-soroban 25 | ``` 26 | 27 | ## Introduction 28 | 29 | Here's some examples on how to use the Soroban helpers: 30 | 31 | ```typescript 32 | import { 33 | getTokenInvocationArgs, 34 | formatTokenAmount, 35 | parseTokenAmount, 36 | scValByType, 37 | } from "@stellar/typescript-wallet-sdk-soroban"; 38 | 39 | const transaction = TransactionBuilder.fromXDR( 40 | "AAAAAgAAAACM6IR9GHiRoVVAO78JJNksy2fKDQNs2jBn8bacsRLcrDucaFsAAAWIAAAAMQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAGAAAAAAAAAABHkEVdJ+UfDnWpBr/qF582IEoDQ0iW0WPzO9CEUdvvh8AAAAIdHJhbnNmZXIAAAADAAAAEgAAAAAAAAAAjOiEfRh4kaFVQDu/CSTZLMtnyg0DbNowZ/G2nLES3KwAAAASAAAAAAAAAADoFl2ACT9HZkbCeuaT9MAIdStpdf58wM3P24nl738AnQAAAAoAAAAAAAAAAAAAAAAAAAAFAAAAAQAAAAAAAAAAAAAAAR5BFXSflHw51qQa/6hefNiBKA0NIltFj8zvQhFHb74fAAAACHRyYW5zZmVyAAAAAwAAABIAAAAAAAAAAIzohH0YeJGhVUA7vwkk2SzLZ8oNA2zaMGfxtpyxEtysAAAAEgAAAAAAAAAA6BZdgAk/R2ZGwnrmk/TACHUraXX+fMDNz9uJ5e9/AJ0AAAAKAAAAAAAAAAAAAAAAAAAABQAAAAAAAAABAAAAAAAAAAIAAAAGAAAAAR5BFXSflHw51qQa/6hefNiBKA0NIltFj8zvQhFHb74fAAAAFAAAAAEAAAAHa35L+/RxV6EuJOVk78H5rCN+eubXBWtsKrRxeLnnpRAAAAACAAAABgAAAAEeQRV0n5R8OdakGv+oXnzYgSgNDSJbRY/M70IRR2++HwAAABAAAAABAAAAAgAAAA8AAAAHQmFsYW5jZQAAAAASAAAAAAAAAACM6IR9GHiRoVVAO78JJNksy2fKDQNs2jBn8bacsRLcrAAAAAEAAAAGAAAAAR5BFXSflHw51qQa/6hefNiBKA0NIltFj8zvQhFHb74fAAAAEAAAAAEAAAACAAAADwAAAAdCYWxhbmNlAAAAABIAAAAAAAAAAOgWXYAJP0dmRsJ65pP0wAh1K2l1/nzAzc/bieXvfwCdAAAAAQBkcwsAACBwAAABKAAAAAAAAB1kAAAAAA==", 41 | Networks.FUTURENET, 42 | ) as Transaction, Operation.InvokeHostFunction[]>; 43 | const op = transaction.operations[0]; 44 | 45 | const args = getTokenInvocationArgs(op); 46 | /* 47 | extracts args from the invoke host function operation: 48 | args = { 49 | fnName: "transfer, 50 | contractId: "CAPECFLUT6KHYOOWUQNP7KC6PTMICKANBURFWRMPZTXUEEKHN67B7UI2", 51 | from: "GCGORBD5DB4JDIKVIA536CJE3EWMWZ6KBUBWZWRQM7Y3NHFRCLOKYVAL", 52 | to: "GDUBMXMABE7UOZSGYJ5ONE7UYAEHKK3JOX7HZQGNZ7NYTZPPP4AJ2GQJ", 53 | amount: 5 54 | } 55 | */ 56 | 57 | const formattedAmount = formatTokenAmount("10000123", 3); 58 | // converts smart contract token amount into a displayable amount that can be 59 | // used on client UI 60 | // formattedAmount = "10000.123" 61 | 62 | const parsedAmount = parseTokenAmount("10000.123", 3); 63 | // converts an amount to a whole (bigint) number that can be used on 64 | // smart contracts operations 65 | // parsedAmount = 10000123 66 | 67 | const accountAddress = xdr.ScVal.scvAddress( 68 | xdr.ScAddress.scAddressTypeAccount( 69 | xdr.PublicKey.publicKeyTypeEd25519( 70 | StrKey.decodeEd25519PublicKey( 71 | "GBBM6BKZPEHWYO3E3YKREDPQXMS4VK35YLNU7NFBRI26RAN7GI5POFBB", 72 | ), 73 | ), 74 | ), 75 | ); 76 | 77 | const addressString = scValByType(accountAddress); 78 | // converts smart contract complex value into a simple string 79 | // addressString = "GBBM6BKZPEHWYO3E3YKREDPQXMS4VK35YLNU7NFBRI26RAN7GI5POFBB" 80 | ``` 81 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/babel.config.js: -------------------------------------------------------------------------------- 1 | const parentConfig = require("../../babel.config"); 2 | 3 | module.exports = { 4 | ...parentConfig, 5 | }; 6 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@stellar/typescript-wallet-sdk-soroban", 3 | "version": "1.9.0", 4 | "engines": { 5 | "node": ">=18" 6 | }, 7 | "browser": "./lib/bundle_browser.js", 8 | "main": "./lib/bundle.js", 9 | "types": "./lib/index.d.ts", 10 | "license": "Apache-2.0", 11 | "private": false, 12 | "devDependencies": { 13 | "@babel/preset-env": "^7.24.0", 14 | "@babel/preset-typescript": "^7.23.3", 15 | "@stellar/prettier-config": "^1.0.1", 16 | "@stellar/tsconfig": "^1.0.2", 17 | "@types/jest": "^29.5.12", 18 | "@typescript-eslint/parser": "^7.1.1", 19 | "babel-jest": "^29.7.0", 20 | "husky": "^9.0.11", 21 | "jest": "^29.7.0", 22 | "npm-run-all": "^4.1.5", 23 | "ts-jest": "^29.1.2", 24 | "ts-loader": "^9.5.1", 25 | "tslib": "^2.6.2", 26 | "typescript": "^5.3.3", 27 | "webpack": "^5.90.3", 28 | "webpack-cli": "^5.1.4" 29 | }, 30 | "dependencies": { 31 | "@stellar/stellar-sdk": "13.0.0-beta.1" 32 | }, 33 | "scripts": { 34 | "prepare": "husky install", 35 | "test": "jest --watchAll", 36 | "test:ci": "jest --ci", 37 | "build:web": "webpack --config webpack.config.js", 38 | "build:node": "webpack --env NODE=true --config webpack.config.js", 39 | "build": "run-p build:web build:node" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/src/Helpers/formatTokenAmount.ts: -------------------------------------------------------------------------------- 1 | import { Soroban } from "@stellar/stellar-sdk"; 2 | 3 | /** 4 | * https://github.com/stellar/js-stellar-base/blob/4b510113738aefb5decb31e2ae72c27da5dd7f5c/src/soroban.js 5 | * 6 | * Given a whole number smart contract amount of a token and an amount of 7 | * decimal places (if the token has any), it returns a "display" value. 8 | * 9 | * All arithmetic inside the contract is performed on integers to avoid 10 | * potential precision and consistency issues of floating-point. 11 | * 12 | * @param {string | bigint} amount the token amount you want to display 13 | * @param {number} decimals specify how many decimal places a token has 14 | * 15 | * @returns {string} the display value 16 | * @throws {TypeError} if the given amount has a decimal point already 17 | * @example 18 | * formatTokenAmount("123000", 4) === "12.3"; 19 | */ 20 | export const formatTokenAmount = ( 21 | amount: string | bigint, 22 | decimals: number, 23 | ): string => Soroban.formatTokenAmount(amount.toString(), decimals); 24 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/src/Helpers/getInvocationDetails.ts: -------------------------------------------------------------------------------- 1 | import { Address, Asset, StrKey, xdr } from "@stellar/stellar-sdk"; 2 | 3 | import { InvocationArgs } from "Types"; 4 | 5 | /** 6 | * Extract invocation args and params from a Soroban authorized invocation 7 | * tree up to its immediate sub invocations. 8 | * 9 | * @param {xdr.SorobanAuthorizedInvocation} invocationTree - The invocation tree. 10 | * 11 | * @returns {InvocationArgs[]} A list of user friendly invocation args and params. 12 | */ 13 | export const getInvocationDetails = ( 14 | invocationTree: xdr.SorobanAuthorizedInvocation, 15 | ): InvocationArgs[] => { 16 | const invocations = [ 17 | getInvocationArgs(invocationTree), 18 | ...invocationTree.subInvocations().map(getInvocationArgs), 19 | ]; 20 | return invocations.filter(isInvocationArg); 21 | }; 22 | 23 | const isInvocationArg = ( 24 | invocation: InvocationArgs | undefined, 25 | ): invocation is InvocationArgs => !!invocation; 26 | 27 | export const getInvocationArgs = ( 28 | invocation: xdr.SorobanAuthorizedInvocation, 29 | ): InvocationArgs | undefined => { 30 | const fn = invocation.function(); 31 | 32 | switch (fn.switch().value) { 33 | // sorobanAuthorizedFunctionTypeContractFn 34 | case 0: { 35 | const _invocation = fn.contractFn(); 36 | const contractId = StrKey.encodeContract( 37 | _invocation.contractAddress().contractId(), 38 | ); 39 | const fnName = _invocation.functionName().toString(); 40 | const args = _invocation.args(); 41 | return { fnName, contractId, args, type: "invoke" }; 42 | } 43 | 44 | // sorobanAuthorizedFunctionTypeCreateContractHostFn 45 | case 1: { 46 | const _invocation = fn.createContractHostFn(); 47 | const [exec, preimage] = [ 48 | _invocation.executable(), 49 | _invocation.contractIdPreimage(), 50 | ]; 51 | 52 | switch (exec.switch().value) { 53 | // contractExecutableWasm 54 | case 0: { 55 | const details = preimage.fromAddress(); 56 | 57 | return { 58 | type: "wasm", 59 | salt: details.salt().toString("hex"), 60 | hash: exec.wasmHash().toString("hex"), 61 | address: Address.fromScAddress(details.address()).toString(), 62 | }; 63 | } 64 | 65 | // contractExecutableStellarAsset 66 | case 1: 67 | return { 68 | type: "sac", 69 | asset: Asset.fromOperation(preimage.fromAsset()).toString(), 70 | }; 71 | 72 | default: 73 | throw new Error(`unknown creation type: ${JSON.stringify(exec)}`); 74 | } 75 | } 76 | 77 | default: { 78 | return undefined; 79 | } 80 | } 81 | }; 82 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/src/Helpers/getTokenInvocationArgs.ts: -------------------------------------------------------------------------------- 1 | import { Operation, StrKey, scValToNative, xdr } from "@stellar/stellar-sdk"; 2 | 3 | import { 4 | ArgsForTokenInvocation, 5 | SorobanTokenInterface, 6 | TokenInvocationArgs, 7 | } from "../Types"; 8 | 9 | export const getArgsForTokenInvocation = ( 10 | fnName: string, 11 | args: xdr.ScVal[], 12 | ): ArgsForTokenInvocation => { 13 | let amount: bigint | number; 14 | let from = ""; 15 | let to = ""; 16 | 17 | switch (fnName) { 18 | case SorobanTokenInterface.transfer: 19 | from = StrKey.encodeEd25519PublicKey( 20 | args[0].address().accountId().ed25519(), 21 | ); 22 | to = StrKey.encodeEd25519PublicKey( 23 | args[1].address().accountId().ed25519(), 24 | ); 25 | amount = scValToNative(args[2]); 26 | break; 27 | case SorobanTokenInterface.mint: 28 | to = StrKey.encodeEd25519PublicKey( 29 | args[0].address().accountId().ed25519(), 30 | ); 31 | amount = scValToNative(args[1]); 32 | break; 33 | default: 34 | amount = BigInt(0); 35 | } 36 | 37 | return { from, to, amount }; 38 | }; 39 | 40 | /** 41 | * Get params and args related to the invoked contract. It must use a valid 42 | * "transfer" or "mint" invocation otherwise it will return 'null'. 43 | * 44 | * @param {Operation.InvokeHostFunction} hostFn - The invoke host function. 45 | * 46 | * @returns {TokenInvocationArgs | null} Params and args related to the 47 | * "transfer" or "mint" invocation like function name, contract id, from/to 48 | * addresses and amount. 49 | */ 50 | export const getTokenInvocationArgs = ( 51 | hostFn: Operation.InvokeHostFunction, 52 | ): TokenInvocationArgs | null => { 53 | if (!hostFn?.func?.invokeContract) { 54 | return null; 55 | } 56 | 57 | let invokedContract: xdr.InvokeContractArgs; 58 | 59 | try { 60 | invokedContract = hostFn.func.invokeContract(); 61 | } catch (e) { 62 | return null; 63 | } 64 | 65 | const contractId = StrKey.encodeContract( 66 | invokedContract.contractAddress().contractId(), 67 | ); 68 | const fnName = invokedContract 69 | .functionName() 70 | .toString() as SorobanTokenInterface; 71 | const args = invokedContract.args(); 72 | 73 | if ( 74 | ![SorobanTokenInterface.transfer, SorobanTokenInterface.mint].includes( 75 | fnName, 76 | ) 77 | ) { 78 | return null; 79 | } 80 | 81 | let opArgs: ArgsForTokenInvocation; 82 | 83 | try { 84 | opArgs = getArgsForTokenInvocation(fnName, args); 85 | } catch (e) { 86 | return null; 87 | } 88 | 89 | return { 90 | fnName, 91 | contractId, 92 | ...opArgs, 93 | }; 94 | }; 95 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/src/Helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./formatTokenAmount"; 2 | export * from "./getInvocationDetails"; 3 | export * from "./getTokenInvocationArgs"; 4 | export * from "./parseTokenAmount"; 5 | export * from "./scValByType"; 6 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/src/Helpers/parseTokenAmount.ts: -------------------------------------------------------------------------------- 1 | import { Soroban } from "@stellar/stellar-sdk"; 2 | import BigNumber from "bignumber.js"; 3 | 4 | /** 5 | * https://github.com/stellar/js-stellar-base/blob/4b510113738aefb5decb31e2ae72c27da5dd7f5c/src/soroban.js 6 | * 7 | * Parse a token amount to use it on smart contract 8 | * 9 | * This function takes the display value and its decimals (if the token has 10 | * any) and returns a string that'll be used within the smart contract. 11 | * 12 | * @param {string | number | BigNumber} amount the token amount you want to 13 | * use in a smart contract which you've been displaying in a UI 14 | * @param {number} decimals the number of decimal places expected in the 15 | * display value (different than the "actual" number, because suffix zeroes 16 | * might not be present) 17 | * 18 | * @returns {bigint} the whole number token amount represented by the display 19 | * value with the decimal places shifted over 20 | * 21 | * @example 22 | * const displayValueAmount = "123.4560" 23 | * const parsedAmtForSmartContract = parseTokenAmount(displayValueAmount, 5); 24 | * parsedAmtForSmartContract === "12345600" 25 | */ 26 | export const parseTokenAmount = ( 27 | amount: string | number | BigNumber, 28 | decimals: number, 29 | ): bigint => { 30 | const parsedAmount = Soroban.parseTokenAmount(amount.toString(), decimals); 31 | 32 | return BigInt(parsedAmount.toString()); 33 | }; 34 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/src/Helpers/scValByType.ts: -------------------------------------------------------------------------------- 1 | import { StrKey, scValToNative, xdr } from "@stellar/stellar-sdk"; 2 | 3 | /* eslint-disable jsdoc/require-returns-type */ 4 | /** 5 | * This function attempts to convert smart contract (complex) value types 6 | * to common/simpler types like string, array, buffer, JSON string, etc. 7 | * 8 | * @param {xdr.ScVal} scVal the smart contract (complex) value 9 | * 10 | * 11 | * @returns the smart contract value converted to a common/simpler 12 | * value like string, array, buffer, JSON string, etc. 13 | * 14 | * @example 15 | * const accountAddress = xdr.ScVal.scvAddress( 16 | * xdr.ScAddress.scAddressTypeAccount( 17 | * xdr.PublicKey.publicKeyTypeEd25519( 18 | * StrKey.decodeEd25519PublicKey("GBBM6BKZPEHWYO3E3YKREDPQXMS4VK35YLNU7NFBRI26RAN7GI5POFBB"), 19 | * ), 20 | * ) 21 | * ); ===> complex object 22 | * 23 | * scValByType(accountAddress) returns "GBBM6BKZPEHWYO3E3YKREDPQXMS4VK35YLNU7NFBRI26RAN7GI5POFBB" 24 | */ 25 | export const scValByType = (scVal: xdr.ScVal) => { 26 | switch (scVal.switch()) { 27 | case xdr.ScValType.scvAddress(): { 28 | const address = scVal.address(); 29 | const addressType = address.switch(); 30 | if (addressType.name === "scAddressTypeAccount") { 31 | return StrKey.encodeEd25519PublicKey(address.accountId().ed25519()); 32 | } 33 | return StrKey.encodeContract(address.contractId()); 34 | } 35 | 36 | case xdr.ScValType.scvBool(): { 37 | return scVal.b(); 38 | } 39 | 40 | case xdr.ScValType.scvBytes(): { 41 | return JSON.stringify(scVal.bytes().toJSON().data); 42 | } 43 | 44 | case xdr.ScValType.scvContractInstance(): { 45 | const instance = scVal.instance(); 46 | return instance.executable().wasmHash()?.toString(); 47 | } 48 | 49 | case xdr.ScValType.scvError(): { 50 | const error = scVal.error(); 51 | return error.value(); 52 | } 53 | 54 | case xdr.ScValType.scvTimepoint(): 55 | case xdr.ScValType.scvDuration(): 56 | case xdr.ScValType.scvI128(): 57 | case xdr.ScValType.scvI256(): 58 | case xdr.ScValType.scvI32(): 59 | case xdr.ScValType.scvI64(): 60 | case xdr.ScValType.scvU128(): 61 | case xdr.ScValType.scvU256(): 62 | case xdr.ScValType.scvU32(): 63 | case xdr.ScValType.scvU64(): { 64 | return scValToNative(scVal).toString(); 65 | } 66 | 67 | case xdr.ScValType.scvLedgerKeyNonce(): 68 | case xdr.ScValType.scvLedgerKeyContractInstance(): { 69 | if (scVal.switch().name === "scvLedgerKeyNonce") { 70 | const val = scVal.nonceKey().nonce(); 71 | return val.toString(); 72 | } 73 | return scVal.value(); 74 | } 75 | 76 | case xdr.ScValType.scvVec(): 77 | case xdr.ScValType.scvMap(): { 78 | return JSON.stringify( 79 | scValToNative(scVal), 80 | (_, val) => (typeof val === "bigint" ? val.toString() : val), 81 | 2, 82 | ); 83 | } 84 | 85 | case xdr.ScValType.scvString(): 86 | case xdr.ScValType.scvSymbol(): { 87 | const native = scValToNative(scVal); 88 | if (native.constructor === "Uint8Array") { 89 | return native.toString(); 90 | } 91 | return native; 92 | } 93 | 94 | default: 95 | return null; 96 | } 97 | }; 98 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/src/Types/index.ts: -------------------------------------------------------------------------------- 1 | import { xdr } from "@stellar/stellar-sdk"; 2 | 3 | // https://github.com/stellar/soroban-examples/blob/main/token/src/contract.rs 4 | export enum SorobanTokenInterface { 5 | transfer = "transfer", 6 | mint = "mint", 7 | } 8 | 9 | export type ArgsForTokenInvocation = { 10 | from: string; 11 | to: string; 12 | amount: bigint | number; 13 | }; 14 | 15 | export type TokenInvocationArgs = ArgsForTokenInvocation & { 16 | fnName: SorobanTokenInterface; 17 | contractId: string; 18 | }; 19 | 20 | export interface FnArgsInvoke { 21 | type: "invoke"; 22 | fnName: string; 23 | contractId: string; 24 | args: xdr.ScVal[]; 25 | } 26 | 27 | export interface FnArgsCreateWasm { 28 | type: "wasm"; 29 | salt: string; 30 | hash: string; 31 | address: string; 32 | } 33 | 34 | export interface FnArgsCreateSac { 35 | type: "sac"; 36 | asset: string; 37 | } 38 | 39 | export type InvocationArgs = FnArgsInvoke | FnArgsCreateWasm | FnArgsCreateSac; 40 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Helpers"; 2 | export * from "./Types"; 3 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "baseUrl": "./", 5 | "outDir": "lib", 6 | "declaration": true, 7 | "declarationDir": "lib" 8 | }, 9 | "include": ["./"] 10 | } 11 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/test/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contract, 3 | Keypair, 4 | StrKey, 5 | hash, 6 | nativeToScVal, 7 | xdr, 8 | } from "@stellar/stellar-sdk"; 9 | 10 | // Returns random public key 11 | export const randomKey = (): string => { 12 | return Keypair.random().publicKey(); 13 | }; 14 | 15 | // Creates a 'n' number of contracts with random ids 16 | export const randomContracts = (n: number) => { 17 | return Array.from(Array(n).keys()).map(() => { 18 | // ezpz method to generate random contract IDs 19 | const buf = hash(Buffer.from(randomKey())); 20 | const contractId = StrKey.encodeContract(buf); 21 | return new Contract(contractId); 22 | }); 23 | }; 24 | 25 | // Returns a SorobanAuthorizedFunction invocation with given args 26 | export const makeInvocation = ( 27 | contract: Contract, 28 | name: string, 29 | ...args: any[] 30 | ): xdr.SorobanAuthorizedFunction => { 31 | return xdr.SorobanAuthorizedFunction.sorobanAuthorizedFunctionTypeContractFn( 32 | new xdr.InvokeContractArgs({ 33 | contractAddress: contract.address().toScAddress(), 34 | functionName: name, 35 | args: args.map((arg) => nativeToScVal(arg)), 36 | }), 37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "baseUrl": "src/", 5 | "outDir": "lib", 6 | "declaration": true, 7 | "declarationDir": "lib" 8 | }, 9 | "include": ["src"] 10 | } 11 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk-soroban/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | 4 | module.exports = (env = { NODE: false }) => { 5 | const isBrowser = !env.NODE; 6 | 7 | return { 8 | mode: "development", 9 | entry: "./src/index.ts", 10 | devtool: "source-map", 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.ts$/, 15 | use: "ts-loader", 16 | exclude: /node_modules/, 17 | }, 18 | ], 19 | }, 20 | resolve: { 21 | extensions: [".js", ".json", ".ts"], 22 | fallback: isBrowser 23 | ? { 24 | crypto: require.resolve("crypto-browserify"), 25 | http: require.resolve("stream-http"), 26 | https: require.resolve("https-browserify"), 27 | stream: require.resolve("stream-browserify"), 28 | url: require.resolve("url"), 29 | util: require.resolve("util"), 30 | vm: require.resolve("vm-browserify"), 31 | "process/browser": require.resolve("process/browser"), 32 | } 33 | : {}, 34 | }, 35 | output: { 36 | library: "WalletSDK", 37 | libraryTarget: "umd", 38 | globalObject: "this", 39 | filename: `bundle${isBrowser ? "_browser" : ""}.js`, 40 | path: path.resolve(__dirname, "lib"), 41 | }, 42 | target: isBrowser ? "web" : "node", 43 | plugins: isBrowser 44 | ? [ 45 | new webpack.ProvidePlugin({ 46 | process: "process/browser", 47 | }), 48 | ] 49 | : [], 50 | }; 51 | }; 52 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/CHANGELOG.MD: -------------------------------------------------------------------------------- 1 | # Release notes - Typescript Wallet SDK - 1.9.0 2 | 3 | ### Added 4 | * Added SEP-43 types: (#186) 5 | 6 | # Release notes - Typescript Wallet SDK - 1.8.0 7 | 8 | ### Added 9 | * Upgrades `@stellar/stellar-sdk` to `13.0.0-beta.1` which supports the `Protocol 22` XDR (#172) 10 | * Add `transaction_id` field to SEP-12 requests (#165) 11 | * Make `withdraw_anchor_account` field optional (#164) 12 | * Deprecate `getServicesInfo()` in favor of `Sep24.info()` (#166) 13 | 14 | # Release notes - Typescript Wallet SDK - 1.7.0 15 | 16 | ### Fixed 17 | * Replace `tweetnacl-util` with `@stablelib` packages (#149) 18 | * this fixes the lib `crashing` on `React Native` environment 19 | * Export missing classes and aliases (#150) 20 | * with this change devs can now access the following classes: 21 | * `Sep6` 22 | * `Sep12` 23 | * `Sep38` 24 | * `StellarAssetId` 25 | * `DomainSigner` 26 | * and aliases: 27 | * `Transfer` (alias for `Sep6` class) 28 | * `Auth` (alias for `Sep10` class) 29 | * `Customer` (alias for `Sep12` class) 30 | * `Interactive` (alias for `Sep24` class) 31 | * `Quote` (alias for `Sep38` class) 32 | * `XLM` (alias for `NativeAssetId` class) 33 | 34 | # Release notes - Typescript Wallet SDK - 1.6.0 35 | 36 | ### Added 37 | * Support for Sep-7 (#141)(#144) 38 | * Upgrade @stellar/stellar-sdk to 12.1.0 (#143) 39 | * New on_hold status (#140) 40 | 41 | ### Fixed 42 | * [BREAKING CHANGE] Fix response objects returned on Customer functions (#142) 43 | 44 | # Release notes - Typescript Wallet SDK - 1.5.0 45 | 46 | ### Added 47 | * AuthHeaderSigner to Authentication Flow 48 | * End to end tests for testing browser build 49 | * Beta builds on merges to develop branch 50 | 51 | # Release notes - Typescript Wallet SDK - 1.4.0 52 | 53 | ### Added 54 | * SEP-10 sign challenge transaction helper 55 | * Anchor platform integration tests 56 | * Change project structure to monorepo 57 | * Helper for parsing AnchorTransaction 58 | 59 | ### Fixed 60 | * Fix stellar-sdk imports 61 | 62 | # Release notes - Typescript Wallet SDK - 1.3.1 63 | 64 | ### Added 65 | * Upgraded stellar-sdk to 11.1.0 66 | 67 | # Release notes - Typescript Wallet SDK - 1.3.0 68 | 69 | ### Added 70 | * Type aliases 71 | * Account setup and recovery using SEP-30 72 | * Customer / SEP-12 code 73 | * SEP-6 deposit and withdrawal 74 | * Exchange endpoints 75 | * Recovery integration tests 76 | * Watcher and polling for SEP-6 77 | * AuthToken class 78 | * Account merge and premade assets 79 | * SEP-38 info, price, and prices 80 | * SEP-38 Quote 81 | 82 | ### Fixed 83 | * Some small fixes to recovery code 84 | 85 | # Release notes - Typescript Wallet SDK - 1.2.1 86 | 87 | ### Fixed 88 | * Better handle axios errors 89 | 90 | # Release notes - Typescript Wallet SDK - 1.2.0 91 | 92 | ### Added 93 | * Sponosring transactions 94 | * Account modification functions 95 | * Default domain signer and default client_domain 96 | * Path payment and swap 97 | * Sep24 example code 98 | 99 | ### Fixed 100 | * Add build for both node and browser 101 | 102 | # Release notes - Typescript Wallet SDK - 1.1.3 103 | 104 | ### Fixed 105 | * Check if withdraw memo is hash type 106 | 107 | ### Added 108 | * Upgrading stellar-sdk veresion for protocol 20 109 | 110 | 111 | # Release notes - Typescript Wallet SDK - 1.1.2 112 | 113 | ### Fixed 114 | * Fix watcher stopping 115 | * Only emit txn if status changed 116 | 117 | 118 | # Release notes - Typescript Wallet SDK - 1.1.0 119 | 120 | ### Added 121 | * Submitting a transaction 122 | * Manage non-XLM trustlines 123 | * Importing and signing arbitrary transactions given an XDR 124 | * Horizon getInfo and gitHistory functions 125 | * Helper method for creating a keypair from random bytes 126 | * Exporting classes 127 | * Transfer withdrawal method 128 | * Fee bump transaction 129 | * Building function to submitWithFeeIncrease 130 | 131 | 132 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/README.md: -------------------------------------------------------------------------------- 1 | # Stellar Typescript Wallet SDK [![npm version](https://badge.fury.io/js/@stellar%2Ftypescript-wallet-sdk.svg)](https://badge.fury.io/js/@stellar%2Ftypescript-wallet-sdk) 2 | 3 | Typescript Wallet SDK is a library that allows developers to build wallet 4 | applications on the Stellar network faster. It utilizes 5 | [Javascript Stellar SDK](https://github.com/stellar/js-stellar-sdk) to 6 | communicate with a Stellar Horizon server. 7 | It offers wide range of functionality to simplify integration with the Stellar 8 | network, and connect to the anchors easily, utilizing various Stellar protocols 9 | (SEPs) 10 | 11 | ## Dependency 12 | 13 | The library is available via npm. To import `typescript-wallet-sdk` library you 14 | need to add it as a dependency to your code: 15 | 16 | yarn: 17 | 18 | ```shell 19 | yarn add @stellar/typescript-wallet-sdk 20 | ``` 21 | 22 | npm: 23 | 24 | ```shell 25 | npm install @stellar/typescript-wallet-sdk 26 | ``` 27 | 28 | ## Introduction 29 | 30 | Here's a small example creating main wallet class with default configuration 31 | connected to testnet network: 32 | 33 | ```typescript 34 | let wallet = walletSdk.Wallet.TestNet(); 35 | ``` 36 | 37 | It should later be re-used across the code, as it has access to various useful 38 | children classes. For example, you can authenticate with the `testanchor` as 39 | simple as: 40 | 41 | ```typescript 42 | const authKey = SigningKeypair.fromSecret("my secret key"); 43 | const anchor = wallet.anchor({ homeDomain: "testanchor.stellar.org" }); 44 | const sep10 = await anchor.sep10(); 45 | 46 | const authToken = await sep10.authenticate({ accountKp: authKey }); 47 | ``` 48 | 49 | Read 50 | [full wallet guide](https://developers.stellar.org/docs/category/build-a-wallet-with-the-wallet-sdk) 51 | for more info 52 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/babel.config.js: -------------------------------------------------------------------------------- 1 | const parentConfig = require("../../babel.config"); 2 | 3 | module.exports = { 4 | ...parentConfig, 5 | }; 6 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/examples/helpers/ask-question.ts: -------------------------------------------------------------------------------- 1 | import readline from "readline"; 2 | 3 | export const askQuestion = (query) => { 4 | const rl = readline.createInterface({ 5 | input: process.stdin, 6 | output: process.stdout, 7 | }); 8 | 9 | return new Promise((resolve) => 10 | rl.question(query, (ans) => { 11 | rl.close(); 12 | resolve(ans); 13 | }), 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/examples/sep10/.env.example: -------------------------------------------------------------------------------- 1 | ANCHOR_DOMAIN=testanchor.stellar.org 2 | RUN_MAINNET=false 3 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/examples/sep10/README.md: -------------------------------------------------------------------------------- 1 | # Sep-10 examples for stellar typescript-wallet-sdk 2 | 3 | ## Example code 4 | 5 | To view how the wallet-sdk can be used to create a sep-10 auth token for a 6 | wallet to send to an anchor, look at `sep10Wallet.ts`. 7 | 8 | To view how to setup an authentication server for the anchor, look at 9 | `sep10Server.ts`. 10 | 11 | ## Running deposit and withdrawals 12 | 13 | To see them in action you can run below from the project root: 14 | 15 | ``` 16 | $ yarn example:sep10 17 | ``` 18 | 19 | This will run an example SEP-10 server which can use to sign authentication 20 | requests and to serve a SEP-1 stellar.toml 21 | 22 | ## Changing environment variables 23 | 24 | If you'd like to use different environment variable values than the default 25 | ones, you can add a `.env` file. See `.env.example` as an example. 26 | 27 | The environment variables you can set are: 28 | 29 | | Variable Name | Description | Default Value | 30 | | --------------- | ------------------------------------------------------------------------------------------------------------------------------ | ---------------------- | 31 | | ANCHOR_DOMAIN | The anchor domain for your application. | testanchor.stellar.org | 32 | | AUTH_KEY_SECRET | The secret key for the account authenticating | none | 33 | | RUN_MAINNET | Set to `true` to run the application on Mainnet. | false | 34 | | CLIENT_DOMAIN | [SEP-10](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md) client domain used for authentication. | none | 35 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/examples/sep10/sep10Server.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import express from "express"; 3 | import { Transaction, Keypair } from "@stellar/stellar-sdk"; 4 | import * as dotenv from "dotenv"; 5 | import bodyParser from "body-parser"; 6 | 7 | dotenv.config({ path: path.resolve(__dirname, ".env") }); 8 | 9 | const PORT = process.env.SERVER_PORT ?? 7000; 10 | const SERVER_SIGNING_KEY = String(process.env.SERVER_SIGNING_KEY); 11 | const app = express(); 12 | 13 | app.use(bodyParser.json()); 14 | app.use(bodyParser.urlencoded({ extended: true })); 15 | 16 | // Serve the sep-1 stellar.toml file 17 | app.use( 18 | "/.well-known", 19 | express.static(path.join(__dirname, "well_known"), { 20 | setHeaders: function (res) { 21 | res.set("Access-Control-Allow-Headers", "Content-Type,X-Requested-With"); 22 | res.type("application/json"); 23 | console.log("request to /.well-known"); 24 | }, 25 | }), 26 | ); 27 | 28 | // Sign requests from the wallet for sep-10 client attribution 29 | app.post("/sign", (req, res) => { 30 | console.log("request to /sign"); 31 | const envelope_xdr = req.body.transaction; 32 | const network_passphrase = req.body.network_passphrase; 33 | const transaction = new Transaction(envelope_xdr, network_passphrase); 34 | 35 | if (Number.parseInt(transaction.sequence, 10) !== 0) { 36 | res.status(400); 37 | res.send("transaction sequence value must be '0'"); 38 | return; 39 | } 40 | 41 | transaction.sign(Keypair.fromSecret(SERVER_SIGNING_KEY)); 42 | 43 | res.set("Access-Control-Allow-Origin", "*"); 44 | res.status(200); 45 | res.send({ 46 | transaction: transaction.toEnvelope().toXDR("base64"), 47 | network_passphrase: network_passphrase, 48 | }); 49 | }); 50 | 51 | app.listen(PORT, () => { 52 | console.log(`Listening on port ${PORT}`); 53 | }); 54 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/examples/sep10/sep10Wallet.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | 3 | import { walletSdk, SigningKeypair, DefaultSigner, Wallet } from "../../src"; 4 | 5 | import * as dotenv from "dotenv"; 6 | dotenv.config({ path: path.resolve(__dirname, ".env") }); 7 | 8 | // Grabbing environment variables 9 | 10 | const anchorDomain = process.env.ANCHOR_DOMAIN || "testanchor.stellar.org"; 11 | const runMainnet = process.env.RUN_MAINNET || false; 12 | const authKeySecret = process.env.AUTH_KEY_SECRET; 13 | const clientDomain = process.env.CLIENT_DOMAIN; 14 | 15 | let wallet: Wallet; 16 | if (runMainnet === "true") { 17 | console.log("Warning: you are running this script on the public network."); 18 | wallet = walletSdk.Wallet.MainNet(); 19 | } else { 20 | wallet = walletSdk.Wallet.TestNet(); 21 | } 22 | const anchor = wallet.anchor({ 23 | homeDomain: anchorDomain, 24 | }); 25 | 26 | const getSep10AuthToken = async () => { 27 | const authKey = SigningKeypair.fromSecret(authKeySecret); 28 | const sep10 = await anchor.sep10(); 29 | const signer = DefaultSigner; 30 | 31 | const getAuthToken = async () => { 32 | return sep10.authenticate({ 33 | accountKp: authKey, 34 | walletSigner: signer, 35 | clientDomain, 36 | }); 37 | }; 38 | 39 | const authToken = await getAuthToken(); 40 | 41 | return authToken; 42 | }; 43 | 44 | const authToken = getSep10AuthToken(); 45 | console.log(`Auth Token: ${authToken}`); 46 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/examples/sep10/well_known/stellar.toml: -------------------------------------------------------------------------------- 1 | ACCOUNTS = [ "GBM53YM5CUICEOTGY3N2UJLLZ4QXNCAANINBNLHSNTEULBQ3HENOUU2J" ] 2 | VERSION = "0.1.0" 3 | SIGNING_KEY = "GBM53YM5CUICEOTGY3N2UJLLZ4QXNCAANINBNLHSNTEULBQ3HENOUU2J" 4 | NETWORK_PASSPHRASE = "Test SDF Network ; September 2015" 5 | 6 | [[PRINCIPALS]] 7 | name = "user" 8 | email = "user@stellar.org" 9 | keybase = "user" 10 | github = "https://www.github.com/user" 11 | 12 | [DOCUMENTATION] 13 | ORG_NAME = "Stellar Development Foundation" 14 | ORG_URL = "https://stellar.org" 15 | ORG_DESCRIPTION = "TS example backend server." 16 | ORG_KEYBASE = "stellar.public" 17 | ORG_TWITTER = "StellarOrg" 18 | ORG_GITHUB = "stellar" -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/examples/sep12/.env.example: -------------------------------------------------------------------------------- 1 | SECRET_KEY="S..." 2 | ANCHOR_DOMAIN=testanchor.stellar.org -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/examples/sep12/README.md: -------------------------------------------------------------------------------- 1 | # Sep-12 examples for stellar typescript-wallet-sdk 2 | 3 | ## Example code 4 | 5 | To view how the wallet-sdk can be used to add sep12 KYC details, look at 6 | `./sep12.ts`. 7 | 8 | To run the script and interactively collect KYC details, configure the 9 | appropriate environment variables and run - 10 | 11 | ``` 12 | $ yarn example:sep12 13 | ``` 14 | 15 | ## Changing environment variables 16 | 17 | If you'd like to use different environment variable values than the default 18 | ones, you can add a `.env` file. See `.env.example` as an example. 19 | 20 | The environment variables you can set are: 21 | 22 | | Variable Name | Description | Default Value | 23 | | ------------- | ---------------------------------------------- | ---------------------- | 24 | | ANCHOR_DOMAIN | The anchor domain for your application. | testanchor.stellar.org | 25 | | SECRET_KEY | The secret key to use for sep10 authentication | | 26 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/examples/sep12/sep12.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | 3 | import * as dotenv from "dotenv"; 4 | import { SigningKeypair, walletSdk } from "../../src"; 5 | import { askQuestion } from "helpers/ask-question"; 6 | 7 | dotenv.config({ path: path.resolve(__dirname, ".env") }); 8 | 9 | const main = async () => { 10 | const homeDomain = process.env.ANCHOR_DOMAIN || "testanchor.stellar.org"; 11 | const secretKey = process.env.SECRET_KEY; 12 | if (!secretKey) { 13 | throw new Error("No secret key provided"); 14 | } 15 | 16 | const wallet = walletSdk.Wallet.TestNet(); 17 | const anchor = wallet.anchor({ homeDomain }); 18 | 19 | const authKey = SigningKeypair.fromSecret(secretKey); 20 | const sep10 = await anchor.sep10(); 21 | const authToken = await sep10.authenticate({ accountKp: authKey }); 22 | const sep12 = await anchor.sep12(authToken); 23 | 24 | const firstName = await askQuestion("What is your first name?"); 25 | const lastName = await askQuestion("What is your last name?"); 26 | 27 | const response = await sep12.add({ 28 | sep9Info: { 29 | first_name: firstName, 30 | last_name: lastName, 31 | // ... 32 | }, 33 | }); 34 | 35 | console.log(response.id); 36 | }; 37 | 38 | main(); 39 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/examples/sep24/.env.example: -------------------------------------------------------------------------------- 1 | ANCHOR_DOMAIN=testanchor.stellar.org 2 | ASSET_CODE=USDC 3 | ASSET_ISSUER=GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5 4 | RUN_MAINNET=false 5 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/examples/sep24/README.md: -------------------------------------------------------------------------------- 1 | # Sep-24 examples for stellar typescript-wallet-sdk 2 | 3 | ## Example code 4 | 5 | To view how the wallet-sdk can be used to create sep-24 deposits and withdrawals 6 | look at `sep24.ts`. 7 | 8 | ## Running deposit and withdrawals 9 | 10 | To see them in action you can run below from the project root: 11 | 12 | ``` 13 | $ yarn example:sep24 14 | ``` 15 | 16 | This will run the deposit flow and watch for it to finish. At the end it will 17 | ask if you'd like to run the withdraw flow. Use USDC as the asset for the 18 | example. 19 | 20 | Progress will be logged in the terminal. 21 | 22 | _note: the identity values used in the sep24 interactive portal can all be fake_ 23 | 24 | ## Changing environment variables 25 | 26 | If you'd like to use different environment variable values than the default 27 | ones, you can add a `.env` file. See `.env.example` as an example. 28 | 29 | The environment variables you can set are: 30 | 31 | | Variable Name | Description | Default Value | 32 | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------- | 33 | | ANCHOR_DOMAIN | The anchor domain for your application. | testanchor.stellar.org | 34 | | ASSET_CODE | The code representing the asset. | USDC | 35 | | ASSET_ISSUER | The issuer's public key for the asset. | GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5 | 36 | | SOURCE_ACCOUNT_SECRET | The secret key for the account depositing or withdrawing from the anchor. If none given then a new account will be created. | none | 37 | | RUN_MAINNET | Set to `true` to run the application on Mainnet. | false | 38 | | CLIENT_DOMAIN | [SEP-10](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md) client domain used for authentication. | none | 39 | | CLIENT_SECRET | Secret key for the client domain. Alternatively, you may want to implement your own `WalletSigner`. | none | 40 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "baseUrl": "./", 5 | "outDir": "lib", 6 | "declaration": true, 7 | "declarationDir": "lib" 8 | }, 9 | "include": ["./"] 10 | } 11 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/jest.e2e.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rootDir: "./", 3 | preset: "ts-jest", 4 | transformIgnorePatterns: [`/node_modules/(?!${["@stablelib"].join("|")})`], 5 | transform: { 6 | "^.+\\.(ts|tsx)?$": "ts-jest", 7 | "^.+\\.(js|jsx)$": "babel-jest", 8 | }, 9 | testMatch: ["**/e2e/*.test.ts"], 10 | }; 11 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/jest.integration.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rootDir: "./", 3 | preset: "ts-jest", 4 | transformIgnorePatterns: [`/node_modules/(?!${["@stablelib"].join("|")})`], 5 | transform: { 6 | "^.+\\.(ts|tsx)?$": "ts-jest", 7 | "^.+\\.(js|jsx)$": "babel-jest", 8 | }, 9 | testMatch: ["**/integration/*.test.ts"], 10 | }; 11 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@stellar/typescript-wallet-sdk", 3 | "version": "1.9.0", 4 | "engines": { 5 | "node": ">=18" 6 | }, 7 | "browser": "./lib/bundle_browser.js", 8 | "main": "./lib/bundle.js", 9 | "types": "./lib/index.d.ts", 10 | "license": "Apache-2.0", 11 | "private": false, 12 | "devDependencies": { 13 | "@babel/preset-env": "^7.20.2", 14 | "@babel/preset-typescript": "^7.23.3", 15 | "@stellar/prettier-config": "^1.0.1", 16 | "@stellar/tsconfig": "^1.0.2", 17 | "@types/jest": "^29.4.0", 18 | "@types/lodash": "^4.14.194", 19 | "@types/sinon": "^10.0.15", 20 | "@typescript-eslint/eslint-plugin": "^6.7.5", 21 | "@typescript-eslint/parser": "^6.7.5", 22 | "babel-jest": "^29.4.1", 23 | "crypto-browserify": "^3.12.0", 24 | "dotenv": "^16.3.1", 25 | "eslint": "^8.51.0", 26 | "eslint-config-prettier": "^9.0.0", 27 | "eslint-plugin-jsdoc": "^46.8.2", 28 | "express": "^4.19.2", 29 | "husky": "^8.0.0", 30 | "jest": "^29.4.1", 31 | "lint-staged": "^14.0.1", 32 | "npm-run-all": "^4.1.5", 33 | "playwright": "^1.43.1", 34 | "prettier": "^2.0.5", 35 | "pretty-quick": "^2.0.1", 36 | "process": "^0.11.10", 37 | "sinon": "^15.1.0", 38 | "stream-browserify": "^3.0.0", 39 | "ts-jest": "^29.0.5", 40 | "ts-loader": "^9.4.2", 41 | "ts-node": "^10.9.1", 42 | "tslib": "^2.5.0", 43 | "typescript": "^5.0.4", 44 | "webpack": "^5.83.1", 45 | "webpack-cli": "^5.1.1" 46 | }, 47 | "dependencies": { 48 | "@stablelib/base64": "^2.0.0", 49 | "@stablelib/utf8": "^2.0.0", 50 | "@stellar/stellar-sdk": "13.0.0-beta.1", 51 | "axios": "^1.4.0", 52 | "base64url": "^3.0.1", 53 | "https-browserify": "^1.0.0", 54 | "jws": "^4.0.0", 55 | "lodash": "^4.17.21", 56 | "query-string": "^7.1.3", 57 | "stream-http": "^3.2.0", 58 | "tweetnacl": "^1.0.3", 59 | "url": "^0.11.0", 60 | "util": "^0.12.5", 61 | "utility-types": "^3.10.0", 62 | "vm-browserify": "^1.1.2" 63 | }, 64 | "scripts": { 65 | "test": "jest --watchAll", 66 | "test:ci": "jest --ci", 67 | "test:e2e:ci": "jest --config jest.e2e.config.js --ci", 68 | "test:recovery:ci": "jest --config jest.integration.config.js recovery.test.ts --ci", 69 | "test:anchorplatform:ci": "yarn jest --config jest.integration.config.js anchorplatform.test.ts --ci", 70 | "build:web": "webpack --config webpack.config.js", 71 | "build:node": "webpack --env NODE=true --config webpack.config.js", 72 | "build": "run-p build:web build:node", 73 | "example:sep10Server": "ts-node examples/sep10/sep10Server.ts", 74 | "example:sep10Wallet": "ts-node examples/sep10/sep10Wallet.ts", 75 | "example:sep24": "ts-node examples/sep24/sep24.ts", 76 | "example:sep12": "ts-node examples/sep12/sep12.ts" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Types 3 | */ 4 | import * as Types from "./walletSdk/Types"; 5 | export { Types }; 6 | 7 | /** 8 | * Classes 9 | */ 10 | export { 11 | Wallet, 12 | Config, 13 | StellarConfiguration, 14 | ApplicationConfiguration, 15 | } from "./walletSdk"; 16 | export { Anchor } from "./walletSdk/Anchor"; 17 | export { Sep6, Transfer } from "./walletSdk/Anchor/Sep6"; 18 | export { Sep24, Interactive } from "./walletSdk/Anchor/Sep24"; 19 | export { Sep38, Quote } from "./walletSdk/Anchor/Sep38"; 20 | export { 21 | StellarAssetId, 22 | IssuedAssetId, 23 | NativeAssetId, 24 | XLM, 25 | FiatAssetId, 26 | } from "./walletSdk/Asset"; 27 | export { 28 | Sep10, 29 | Auth, 30 | WalletSigner, 31 | DefaultSigner, 32 | DomainSigner, 33 | } from "./walletSdk/Auth"; 34 | export { 35 | AuthHeaderSigner, 36 | DefaultAuthHeaderSigner, 37 | DomainAuthHeaderSigner, 38 | } from "./walletSdk/Auth/AuthHeaderSigner"; 39 | export { Sep12, Customer } from "./walletSdk/Customer"; 40 | export { 41 | AccountKeypair, 42 | PublicKeypair, 43 | SigningKeypair, 44 | AccountService, 45 | Stellar, 46 | CommonTransactionBuilder, 47 | TransactionBuilder, 48 | SponsoringBuilder, 49 | } from "./walletSdk/Horizon"; 50 | export { Recovery } from "./walletSdk/Recovery"; 51 | export { 52 | Sep7Base, 53 | Sep7Pay, 54 | Sep7Tx, 55 | isValidSep7Uri, 56 | parseSep7Uri, 57 | sep7ReplacementsFromString, 58 | sep7ReplacementsToString, 59 | } from "./walletSdk/Uri"; 60 | export { Watcher } from "./walletSdk/Watcher"; 61 | 62 | /** 63 | * Utils 64 | */ 65 | import * as Utils from "./walletSdk/Utils"; 66 | export { Utils }; 67 | 68 | /** 69 | * Exceptions 70 | */ 71 | import * as Exceptions from "./walletSdk/Exceptions"; 72 | export { Exceptions }; 73 | 74 | /** 75 | * Server 76 | */ 77 | import * as Server from "./walletSdk/Server"; 78 | export { Server }; 79 | 80 | import * as walletSdk from "./walletSdk"; 81 | import { Keypair } from "@stellar/stellar-sdk"; 82 | // TODO - figure out why Keypair used in parent codebase throws error 83 | export { walletSdk, Keypair }; 84 | export default { walletSdk }; 85 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Asset/index.ts: -------------------------------------------------------------------------------- 1 | import { Asset as StellarAsset } from "@stellar/stellar-sdk"; 2 | 3 | const STELLAR_SCHEME = "stellar"; 4 | 5 | const FIAT_SCHEME = "iso4217"; 6 | 7 | class AssetId { 8 | id: string; 9 | scheme: string; 10 | get sep38() { 11 | return `${this.scheme}:${this.id}`; 12 | } 13 | } 14 | 15 | export class StellarAssetId extends AssetId { 16 | id: string; 17 | code: string; 18 | issuer: string; 19 | scheme = STELLAR_SCHEME; 20 | 21 | toAsset(): StellarAsset { 22 | if (this.id === "native") { 23 | return new StellarAsset("XLM"); 24 | } 25 | return new StellarAsset(this.code, this.issuer); 26 | } 27 | } 28 | 29 | export class IssuedAssetId extends StellarAssetId { 30 | constructor(code: string, issuer: string) { 31 | super(); 32 | this.code = code; 33 | this.issuer = issuer; 34 | this.id = `${this.code}:${this.issuer}`; 35 | } 36 | 37 | toString() { 38 | return this.sep38; 39 | } 40 | } 41 | 42 | /** 43 | * @alias XLM alias for NativeAssetId class. 44 | */ 45 | export type XLM = NativeAssetId; 46 | 47 | export class NativeAssetId extends StellarAssetId { 48 | id = "native"; 49 | code = "XLM"; 50 | } 51 | 52 | export class FiatAssetId extends AssetId { 53 | id: string; 54 | scheme = FIAT_SCHEME; 55 | constructor(code: string) { 56 | super(); 57 | this.id = code; 58 | } 59 | 60 | toString() { 61 | return this.sep38; 62 | } 63 | } 64 | 65 | export const Assets = { 66 | Main: { 67 | USDC: new IssuedAssetId( 68 | "USDC", 69 | "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN", 70 | ), 71 | }, 72 | Test: { 73 | USDC: new IssuedAssetId( 74 | "USDC", 75 | "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", 76 | ), 77 | }, 78 | }; 79 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Auth/AuthHeaderSigner.ts: -------------------------------------------------------------------------------- 1 | import { AxiosInstance } from "axios"; 2 | import { encode as utf8Encode } from "@stablelib/utf8"; 3 | import { StrKey } from "@stellar/stellar-sdk"; 4 | import nacl from "tweetnacl"; 5 | import base64url from "base64url"; 6 | 7 | import { SigningKeypair } from "../Horizon/Account"; 8 | import { DefaultClient } from "../"; 9 | import { AuthHeaderClaims, AuthHeaderCreateTokenParams } from "../Types"; 10 | import { 11 | AuthHeaderSigningKeypairRequiredError, 12 | AuthHeaderClientDomainRequiredError, 13 | } from "../Exceptions"; 14 | 15 | export interface AuthHeaderSigner { 16 | createToken({ 17 | claims, 18 | clientDomain, 19 | issuer, 20 | }: AuthHeaderCreateTokenParams): Promise; 21 | } 22 | 23 | /** 24 | * Signer for signing JWT for GET /Auth with a custodial private key 25 | * 26 | * @class 27 | */ 28 | export class DefaultAuthHeaderSigner implements AuthHeaderSigner { 29 | expiration: number; 30 | 31 | constructor(expiration: number = 900) { 32 | this.expiration = expiration; 33 | } 34 | 35 | /** 36 | * Create a signed JWT for the auth header 37 | * @constructor 38 | * @param {AuthHeaderCreateTokenParams} params - The create token parameters 39 | * @param {AuthHeaderClaims} params.claims - the data to be signed in the JWT 40 | * @param {string} [params.clientDomain] - the client domain hosting SEP-1 toml 41 | * @param {AccountKeypair} [params.issuer] - the account signing the JWT 42 | * @returns {Promise} The signed JWT 43 | */ 44 | // eslint-disable-next-line @typescript-eslint/require-await 45 | async createToken({ 46 | claims, 47 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 48 | clientDomain, 49 | issuer, 50 | }: AuthHeaderCreateTokenParams): Promise { 51 | if (!(issuer instanceof SigningKeypair)) { 52 | throw new AuthHeaderSigningKeypairRequiredError(); 53 | } 54 | 55 | const issuedAt = claims.iat || Math.floor(Date.now() / 1000); 56 | const timeExp = 57 | claims.exp || Math.floor(Date.now() / 1000) + this.expiration; 58 | 59 | // turn stellar kp into nacl kp for creating JWT 60 | const rawSeed = StrKey.decodeEd25519SecretSeed(issuer.secretKey); 61 | const naclKP = nacl.sign.keyPair.fromSeed(rawSeed); 62 | 63 | // encode JWT message 64 | const header = { alg: "EdDSA" }; 65 | const encodedHeader = base64url(JSON.stringify(header)); 66 | const encodedPayload = base64url( 67 | JSON.stringify({ ...claims, exp: timeExp, iat: issuedAt }), 68 | ); 69 | const utf8Jwt = utf8Encode(`${encodedHeader}.${encodedPayload}`); 70 | 71 | // sign JWT and create signature 72 | const signature = nacl.sign.detached(utf8Jwt, naclKP.secretKey); 73 | const encodedSignature = base64url(Buffer.from(signature)); 74 | 75 | const jwt = `${encodedHeader}.${encodedPayload}.${encodedSignature}`; 76 | return jwt; 77 | } 78 | } 79 | 80 | /** 81 | * Signer for signing JWT for GET /Auth using a remote server to sign. 82 | * 83 | * @class 84 | */ 85 | export class DomainAuthHeaderSigner implements AuthHeaderSigner { 86 | signerUrl: string; 87 | expiration: number; 88 | httpClient: AxiosInstance; 89 | 90 | constructor( 91 | signerUrl: string, 92 | expiration: number = 900, 93 | httpClient?: AxiosInstance, 94 | ) { 95 | this.signerUrl = signerUrl; 96 | this.expiration = expiration; 97 | this.httpClient = httpClient || DefaultClient; 98 | } 99 | 100 | /** 101 | * Create a signed JWT for the auth header by using a remote server to sign the JWT 102 | * @constructor 103 | * @param {AuthHeaderCreateTokenParams} params - The create token parameters 104 | * @param {AuthHeaderClaims} params.claims - the data to be signed in the JWT 105 | * @param {string} [params.clientDomain] - the client domain hosting SEP-1 toml 106 | * @param {AccountKeypair} [params.issuer] - unused, will not be used to sign 107 | * @returns {Promise} The signed JWT 108 | */ 109 | async createToken({ 110 | claims, 111 | clientDomain, 112 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 113 | issuer, 114 | }: AuthHeaderCreateTokenParams): Promise { 115 | if (!clientDomain) { 116 | throw new AuthHeaderClientDomainRequiredError(); 117 | } 118 | 119 | const issuedAt = Math.floor(Date.now() / 1000); 120 | const expiration = Math.floor(Date.now() / 1000) + this.expiration; 121 | 122 | return await this.signTokenRemote({ 123 | claims, 124 | clientDomain, 125 | expiration, 126 | issuedAt, 127 | }); 128 | } 129 | 130 | /** 131 | * Sign JWT by calling a remote server 132 | * @constructor 133 | * @param {SignTokenRemoteParams} params - the sign token params 134 | * @param {AuthHeaderClaims} params.claims - the data to be signed in the JWT 135 | * @param {string} params.clientDomain - the client domain hosting SEP-1 toml 136 | * @param {number} params.expiration - when the token should expire 137 | * @param {number} params.issuedAt - when the token was created 138 | * @returns {Promise} The signed JWT 139 | */ 140 | async signTokenRemote({ 141 | claims, 142 | clientDomain, 143 | expiration, 144 | issuedAt, 145 | }: { 146 | claims: AuthHeaderClaims; 147 | clientDomain: string; 148 | expiration: number; 149 | issuedAt: number; 150 | }): Promise { 151 | const resp = await this.httpClient.post(this.signerUrl, { 152 | clientDomain, 153 | expiration, 154 | issuedAt, 155 | ...claims, 156 | }); 157 | 158 | return resp.data.token; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Auth/WalletSigner.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Transaction, 3 | TransactionBuilder as StellarTransactionBuilder, 4 | } from "@stellar/stellar-sdk"; 5 | import { AxiosInstance } from "axios"; 6 | 7 | import { 8 | SignWithClientAccountParams, 9 | SignWithDomainAccountParams, 10 | HttpHeaders, 11 | } from "../Types"; 12 | import { DefaultClient } from "../"; 13 | import { DefaultSignerDomainAccountError } from "../Exceptions"; 14 | 15 | /** 16 | * A Wallet Signer for signing Stellar transactions. 17 | */ 18 | export interface WalletSigner { 19 | /** 20 | * Sign a transaction with a client keypair. 21 | * @param {Transaction} params.transaction - The transaction to sign. 22 | * @param {AccountKeypair} params.accountKp - The keypair to sign with. 23 | * @returns {Transaction} The signed transaction. 24 | */ 25 | signWithClientAccount({ 26 | transaction, 27 | accountKp, 28 | }: SignWithClientAccountParams): Transaction; 29 | 30 | /** 31 | * Sign a transaction using the domain account's keypair. This method is async in the 32 | * case of signing with a different server. 33 | * @param {XdrEncodedTransaction} params.transactionXDR - The XDR representation of the transaction to sign. 34 | * @param {NetworkPassphrase} params.networkPassphrase - The network passphrase for the Stellar network. 35 | * @param {AccountKeypair} params.accountKp - The keypair of the domain account. 36 | * @returns {Promise} The signed transaction. 37 | */ 38 | signWithDomainAccount({ 39 | transactionXDR, 40 | networkPassphrase, 41 | accountKp, 42 | }: SignWithDomainAccountParams): Promise; 43 | } 44 | 45 | /** 46 | * A Default signer used if no signer is given. 47 | */ 48 | export const DefaultSigner: WalletSigner = { 49 | signWithClientAccount: ({ transaction, accountKp }) => { 50 | transaction.sign(accountKp.keypair); 51 | return transaction; 52 | }, 53 | // eslint-disable-next-line @typescript-eslint/require-await 54 | signWithDomainAccount: async () => { 55 | // The DefaultSigner can't sign transactions with domain account 56 | throw new DefaultSignerDomainAccountError(); 57 | }, 58 | }; 59 | 60 | /** 61 | * A Domain Signer used for signing Stellar transactions with a domain server. 62 | * @class 63 | * @implements {WalletSigner} 64 | */ 65 | export class DomainSigner implements WalletSigner { 66 | private url: string; 67 | private client: AxiosInstance; 68 | private headers: HttpHeaders; 69 | 70 | /** 71 | * Create a new instance of the DomainSigner class. 72 | * @constructor 73 | * @param {string} url - The URL of the domain server. 74 | * @param {HttpHeaders} headers - The HTTP headers for requests to the domain server. 75 | * These headers can be used for authentication purposes. 76 | */ 77 | constructor(url: string, headers: HttpHeaders) { 78 | this.url = url; 79 | this.client = DefaultClient; 80 | this.headers = headers; 81 | } 82 | 83 | signWithClientAccount({ 84 | transaction, 85 | accountKp, 86 | }: SignWithClientAccountParams): Transaction { 87 | transaction.sign(accountKp.keypair); 88 | return transaction; 89 | } 90 | 91 | async signWithDomainAccount({ 92 | transactionXDR, 93 | networkPassphrase, 94 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 95 | accountKp, 96 | }: SignWithDomainAccountParams): Promise { 97 | const response = await this.client.post( 98 | this.url, 99 | { 100 | transactionXDR, 101 | networkPassphrase, 102 | }, 103 | { headers: this.headers }, 104 | ); 105 | 106 | return StellarTransactionBuilder.fromXDR( 107 | response.data.transaction, 108 | networkPassphrase, 109 | ) as Transaction; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Horizon/Account.ts: -------------------------------------------------------------------------------- 1 | import { Keypair, Transaction, FeeBumpTransaction } from "@stellar/stellar-sdk"; 2 | import { SigningKeypairMissingSecretError } from "../Exceptions"; 3 | 4 | export class AccountKeypair { 5 | keypair: Keypair; 6 | constructor(keypair: Keypair) { 7 | this.keypair = keypair; 8 | } 9 | 10 | get publicKey(): string { 11 | return this.keypair.publicKey(); 12 | } 13 | 14 | toString(): string { 15 | return `${this.constructor.name}(address=${this.publicKey})`; 16 | } 17 | } 18 | 19 | export class PublicKeypair extends AccountKeypair { 20 | constructor(keypair: Keypair) { 21 | super(keypair); 22 | } 23 | 24 | static fromPublicKey = (publicKey: string): PublicKeypair => { 25 | return new PublicKeypair(Keypair.fromPublicKey(publicKey)); 26 | }; 27 | } 28 | 29 | export class SigningKeypair extends AccountKeypair { 30 | constructor(keypair: Keypair) { 31 | if (!keypair.canSign()) { 32 | throw new SigningKeypairMissingSecretError(); 33 | } 34 | super(keypair); 35 | } 36 | 37 | static fromSecret = (secretKey: string): SigningKeypair => { 38 | return new SigningKeypair(Keypair.fromSecret(secretKey)); 39 | }; 40 | 41 | get secretKey(): string { 42 | return this.keypair.secret(); 43 | } 44 | 45 | sign( 46 | transaction: Transaction | FeeBumpTransaction, 47 | ): Transaction | FeeBumpTransaction { 48 | transaction.sign(this.keypair); 49 | return transaction; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Horizon/AccountService.ts: -------------------------------------------------------------------------------- 1 | import { Keypair, Networks, Horizon } from "@stellar/stellar-sdk"; 2 | 3 | import { Config } from "../"; 4 | import { SigningKeypair } from "./Account"; 5 | import { 6 | HORIZON_LIMIT_DEFAULT, 7 | HORIZON_LIMIT_MAX, 8 | HORIZON_ORDER, 9 | } from "../Types"; 10 | import { OperationsLimitExceededError } from "../Exceptions"; 11 | 12 | // Do not create this object directly, use the Wallet class. 13 | /** 14 | * KYC management with Sep-12. 15 | * @see {@link https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0012.md} 16 | * Do not create this object directly, use the Stellar class. 17 | * @class 18 | */ 19 | export class AccountService { 20 | private server: Horizon.Server; 21 | private network: Networks; 22 | 23 | /** 24 | * Creates a new instance of the AccountService class. 25 | * @constructor 26 | * @param {Config} cfg - Configuration for the service. 27 | */ 28 | constructor(cfg: Config) { 29 | this.server = cfg.stellar.server; 30 | this.network = cfg.stellar.network; 31 | } 32 | 33 | /** 34 | * Generate new account keypair (public and secret key). This key pair can be 35 | * used to create a Stellar account. 36 | * @returns {SigningKeypair} Keypair capable of signing. 37 | */ 38 | createKeypair(): SigningKeypair { 39 | return new SigningKeypair(Keypair.random()); 40 | } 41 | 42 | /** 43 | * Generate new account keypair (public and secret key) from random bytes. This key pair can be 44 | * used to create a Stellar account. 45 | * @param {Buffer} randomBytes - Random bytes to create keypair from. 46 | * @returns {SigningKeypair} Keypair capable of signing. 47 | */ 48 | createKeypairFromRandom(randomBytes: Buffer): SigningKeypair { 49 | return new SigningKeypair(Keypair.fromRawEd25519Seed(randomBytes)); 50 | } 51 | 52 | /** 53 | * Get account information from the Stellar network. 54 | * @param {object} params - The parameters for retrieving account information. 55 | * @param {string} params.accountAddress - Stellar address of the account. 56 | * @param {Horizon.HorizonApi.Server} [params.serverInstance=this.server] - Horizon server instance when default doesn't work. 57 | * @returns {Promise} Account information. 58 | */ 59 | async getInfo({ 60 | accountAddress, 61 | serverInstance = this.server, 62 | }: { 63 | accountAddress: string; 64 | serverInstance?: Horizon.Server; 65 | }): Promise { 66 | return serverInstance.accounts().accountId(accountAddress).call(); 67 | } 68 | 69 | /** 70 | * Get account operations for the specified Stellar address. 71 | * @param {object} params - The parameters for retrieving account history. 72 | * @param {string} params.accountAddress - Stellar address of the account 73 | * @param {number} [params.limit=HORIZON_LIMIT_DEFAULT] - How many operations to fetch, maximum is 200, default is 10 74 | * @param {HORIZON_ORDER} [params.order=HORIZON_ORDER.DESC] - Data order, ascending or descending, defaults to descending 75 | * @param {string} [params.cursor] - Cursor to specify a starting point 76 | * @param {boolean} [params.includeFailed=false] - Flag to include failed operations. 77 | * @returns {Promise>} A list of operations. 78 | * @throws {OperationsLimitExceededError} when maximum limit of 200 is exceeded. 79 | */ 80 | async getHistory({ 81 | accountAddress, 82 | limit = HORIZON_LIMIT_DEFAULT, 83 | order = HORIZON_ORDER.DESC, 84 | cursor, 85 | includeFailed = false, 86 | }: { 87 | accountAddress: string; 88 | limit?: number; 89 | order?: HORIZON_ORDER; 90 | cursor?: string; 91 | includeFailed?: boolean; 92 | }): Promise< 93 | Horizon.ServerApi.CollectionPage 94 | > { 95 | if (limit > HORIZON_LIMIT_MAX) { 96 | throw new OperationsLimitExceededError(HORIZON_LIMIT_MAX); 97 | } 98 | 99 | return this.server 100 | .operations() 101 | .forAccount(accountAddress) 102 | .limit(limit) 103 | .order(order) 104 | .cursor(cursor) 105 | .includeFailed(includeFailed) 106 | .call(); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Horizon/Transaction/CommonTransactionBuilder.ts: -------------------------------------------------------------------------------- 1 | import { Operation, xdr } from "@stellar/stellar-sdk"; 2 | import { IssuedAssetId } from "../../Asset"; 3 | import { AccountKeypair } from "../Account"; 4 | 5 | export abstract class CommonTransactionBuilder { 6 | protected sourceAddress: string; 7 | protected operations: Array; 8 | 9 | constructor(sourceAddress: string, operations: Array) { 10 | this.sourceAddress = sourceAddress; 11 | this.operations = operations; 12 | } 13 | 14 | /** 15 | * Add a trustline for an asset so can receive or send it. 16 | * @param {IssuedAssetId} asset - The asset for which support is added. 17 | * @param {string} [trustLimit] - The trust limit for the asset. 18 | * @returns {T} The builder class instance called with. 19 | */ 20 | addAssetSupport(asset: IssuedAssetId, trustLimit?: string): T { 21 | this.operations.push( 22 | Operation.changeTrust({ 23 | asset: asset.toAsset(), 24 | limit: trustLimit, 25 | source: this.sourceAddress, 26 | }), 27 | ); 28 | return this as unknown as T; 29 | } 30 | 31 | /** 32 | * Remove a trustline for an asset. 33 | * @param {IssuedAssetId} asset - The asset for which support is added. 34 | * @returns {T} The builder class instance called with. 35 | */ 36 | removeAssetSupport(asset: IssuedAssetId): T { 37 | return this.addAssetSupport(asset, "0"); 38 | } 39 | 40 | /** 41 | * Add a signer to the account. 42 | * @see {@link https://developers.stellar.org/docs/encyclopedia/signatures-multisig} 43 | * @param {AccountKeypair} signerAddress - The new account being added 44 | * @param {number} signerWeight - The weight given to the new signer. 45 | * @returns {T} The builder class instance called with. 46 | */ 47 | addAccountSigner(signerAddress: AccountKeypair, signerWeight: number): T { 48 | this.operations.push( 49 | Operation.setOptions({ 50 | source: this.sourceAddress, 51 | signer: { 52 | ed25519PublicKey: signerAddress.publicKey, 53 | weight: signerWeight, 54 | }, 55 | }), 56 | ); 57 | return this as unknown as T; 58 | } 59 | 60 | /** 61 | * Removes a signer from an account. 62 | * @see {@link https://developers.stellar.org/docs/encyclopedia/signatures-multisig} 63 | * @param {AccountKeypair} signerAddress - The new account being added 64 | * @returns {T} The builder class instance called with. 65 | */ 66 | removeAccountSigner(signerAddress: AccountKeypair): T { 67 | return this.addAccountSigner(signerAddress, 0); 68 | } 69 | 70 | /** 71 | * Locking an account by setting the master key weight to 0. 72 | * Be careful, if no other signers then the account will be locked and unable to 73 | * sign new transactions permanently. 74 | * @returns {T} The builder class instance called with. 75 | */ 76 | lockAccountMasterKey(): T { 77 | this.operations.push( 78 | Operation.setOptions({ 79 | source: this.sourceAddress, 80 | masterWeight: 0, 81 | }), 82 | ); 83 | return this as unknown as T; 84 | } 85 | 86 | /** 87 | * Set thesholds for an account. 88 | * @see {@link https://developers.stellar.org/docs/encyclopedia/signatures-multisig#thresholds} 89 | * @param {object} options - The threshold options. 90 | * @param {number} [options.low] - The low theshold level. 91 | * @param {number} [options.medium] - The medium theshold level. 92 | * @param {number} [options.high] - The high theshold level. 93 | * @returns {T} The builder class instance called with. 94 | */ 95 | setThreshold({ 96 | low, 97 | medium, 98 | high, 99 | }: { 100 | low?: number; 101 | medium?: number; 102 | high?: number; 103 | }): T { 104 | this.operations.push( 105 | Operation.setOptions({ 106 | source: this.sourceAddress, 107 | lowThreshold: low, 108 | medThreshold: medium, 109 | highThreshold: high, 110 | }), 111 | ); 112 | return this as unknown as T; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Horizon/Transaction/SponsoringBuilder.ts: -------------------------------------------------------------------------------- 1 | import { Operation, xdr } from "@stellar/stellar-sdk"; 2 | 3 | import { CommonTransactionBuilder } from "./CommonTransactionBuilder"; 4 | import { AccountKeypair } from "../Account"; 5 | 6 | /** 7 | * Used for building transactions that will include a sponsor. 8 | * Do not create this object directly, use the TransactionBuilder class to create. 9 | * @class 10 | */ 11 | export class SponsoringBuilder extends CommonTransactionBuilder { 12 | private sponsorAccount: AccountKeypair; 13 | 14 | /** 15 | * Creates a new instance of the SponsoringBuilder class. 16 | * @constructor 17 | * @param {string} sponsoredAddress - The address of the account being sponsored. 18 | * @param {AccountKeypair} sponsorAccount - The sponsor account keypair. 19 | * @param {xdr.Operation[]} operations - An array of Stellar operations. 20 | * @param {(builder: SponsoringBuilder) => SponsoringBuilder} buildingFunction - Function for creating the 21 | * operations that will be sponsored. 22 | */ 23 | constructor( 24 | sponsoredAddress: string, 25 | sponsorAccount: AccountKeypair, 26 | operations: Array, 27 | buildingFunction: (builder: SponsoringBuilder) => SponsoringBuilder, 28 | ) { 29 | super(sponsoredAddress, operations); 30 | this.sponsorAccount = sponsorAccount; 31 | 32 | this.startSponsoring(); 33 | buildingFunction(this); 34 | this.stopSponsoring(); 35 | } 36 | 37 | /** 38 | * Creates a Stellar account with sponsored reserves. 39 | * @param {AccountKeypair} newAccount - The new account's keypair. 40 | * @param {number} [startingBalance=0] - The starting balance for the new account (default is 0 XLM). 41 | * @returns {SponsoringBuilder} The SponsoringBuilder instance. 42 | */ 43 | createAccount( 44 | newAccount: AccountKeypair, 45 | startingBalance: number = 0, 46 | ): SponsoringBuilder { 47 | this.operations.push( 48 | Operation.createAccount({ 49 | destination: newAccount.publicKey, 50 | startingBalance: startingBalance.toString(), 51 | source: this.sponsorAccount.publicKey, 52 | }), 53 | ); 54 | return this; 55 | } 56 | 57 | /** 58 | * Start sponsoring the future reserves of an account. 59 | * @returns {void} 60 | */ 61 | startSponsoring() { 62 | this.operations.push( 63 | Operation.beginSponsoringFutureReserves({ 64 | sponsoredId: this.sourceAddress, 65 | source: this.sponsorAccount.publicKey, 66 | }), 67 | ); 68 | } 69 | 70 | /** 71 | * Stop sponsoring the future reserves of an account. 72 | * @returns {void} 73 | */ 74 | stopSponsoring() { 75 | this.operations.push( 76 | Operation.endSponsoringFutureReserves({ 77 | source: this.sourceAddress, 78 | }), 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Horizon/index.ts: -------------------------------------------------------------------------------- 1 | export { AccountKeypair, PublicKeypair, SigningKeypair } from "./Account"; 2 | export { AccountService } from "./AccountService"; 3 | export { Stellar } from "./Stellar"; 4 | export { CommonTransactionBuilder } from "./Transaction/CommonTransactionBuilder"; 5 | export { TransactionBuilder } from "./Transaction/TransactionBuilder"; 6 | export { SponsoringBuilder } from "./Transaction/SponsoringBuilder"; 7 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Server/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Code in the Server module is written to be used by server side 3 | * applications. 4 | */ 5 | 6 | import { 7 | Transaction, 8 | TransactionBuilder, 9 | Keypair, 10 | StellarToml, 11 | } from "@stellar/stellar-sdk"; 12 | 13 | import { parseToml } from "../Utils"; 14 | import { 15 | SignChallengeTxnParams, 16 | SignChallengeTxnResponse, 17 | AnchorTransaction, 18 | WithdrawTransaction, 19 | DepositTransaction, 20 | ErrorTransaction, 21 | } from "../Types"; 22 | import { 23 | ChallengeTxnIncorrectSequenceError, 24 | ChallengeTxnInvalidSignatureError, 25 | UnknownAnchorTransactionError, 26 | InvalidJsonError, 27 | } from "../Exceptions"; 28 | 29 | /** 30 | * Helper method for signing a SEP-10 challenge transaction if valid. 31 | * @param {SignChallengeTxnParams} params - The Authentication params. 32 | * @param {AccountKeypair} params.accountKp - Keypair for the Stellar account signing the transaction. 33 | * @param {string} [params.challengeTx] - The challenge transaction given by an anchor for authentication. 34 | * @param {string} [params.networkPassphrase] - The network passphrase for the network authenticating on. 35 | * @param {string} [params.anchorDomain] - Domain hosting stellar.toml file containing `SIGNING_KEY`. 36 | * @returns {Promise} The signed transaction. 37 | */ 38 | export const signChallengeTransaction = async ({ 39 | accountKp, 40 | challengeTx, 41 | networkPassphrase, 42 | anchorDomain, 43 | }: SignChallengeTxnParams): Promise => { 44 | const tx = TransactionBuilder.fromXDR( 45 | challengeTx, 46 | networkPassphrase, 47 | ) as Transaction; 48 | 49 | if (parseInt(tx.sequence) !== 0) { 50 | throw new ChallengeTxnIncorrectSequenceError(); 51 | } 52 | 53 | const tomlResp = await StellarToml.Resolver.resolve(anchorDomain); 54 | const parsedToml = parseToml(tomlResp); 55 | const anchorKp = Keypair.fromPublicKey(parsedToml.signingKey); 56 | 57 | const isValid = 58 | tx.signatures.length && 59 | anchorKp.verify(tx.hash(), tx.signatures[0].signature()); 60 | if (!isValid) { 61 | throw new ChallengeTxnInvalidSignatureError(); 62 | } 63 | 64 | accountKp.sign(tx); 65 | return { 66 | transaction: tx.toXDR(), 67 | networkPassphrase, 68 | }; 69 | }; 70 | 71 | /** 72 | * Helper method for parsing a JSON string into an AnchorTransaction. 73 | * @param {string} transaction - The json string of an anchor transaction. 74 | * @returns {AnchorTransaction} The transaction object. 75 | */ 76 | export const parseAnchorTransaction = ( 77 | transaction: string, 78 | ): AnchorTransaction => { 79 | let parsed; 80 | try { 81 | parsed = JSON.parse(transaction); 82 | } catch (e) { 83 | throw new InvalidJsonError(); 84 | } 85 | 86 | if (parsed.kind === "withdrawal") { 87 | return parsed as WithdrawTransaction; 88 | } else if (parsed.kind === "deposit") { 89 | return parsed as DepositTransaction; 90 | } else if (parsed.status === "error") { 91 | return parsed as ErrorTransaction; 92 | } else { 93 | throw new UnknownAnchorTransactionError(); 94 | } 95 | }; 96 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Types/auth.ts: -------------------------------------------------------------------------------- 1 | import { Transaction } from "@stellar/stellar-sdk"; 2 | import { decode } from "jws"; 3 | 4 | import { WalletSigner } from "../Auth/WalletSigner"; 5 | import { AccountKeypair, SigningKeypair } from "../Horizon/Account"; 6 | import { AuthHeaderSigner } from "../Auth/AuthHeaderSigner"; 7 | 8 | export type AuthenticateParams = { 9 | accountKp: AccountKeypair; 10 | walletSigner?: WalletSigner; 11 | memoId?: string; 12 | clientDomain?: string; 13 | authHeaderSigner?: AuthHeaderSigner; 14 | }; 15 | 16 | export class AuthToken { 17 | public token: string; 18 | public issuer: string; 19 | public issuedAt: string; 20 | public expiresAt: string; 21 | public clientDomain: string; 22 | private principalAccount: string; 23 | 24 | get account(): string { 25 | return this.principalAccount.split(":")[0]; 26 | } 27 | 28 | get memo(): string { 29 | const split = this.principalAccount.split(":"); 30 | if (split.length !== 2) { 31 | return null; 32 | } 33 | return split[1]; 34 | } 35 | 36 | static from = (str: string): AuthToken => { 37 | const authToken = new AuthToken(); 38 | 39 | const decoded = decode(str); 40 | authToken.issuer = decoded.payload.iss; 41 | authToken.principalAccount = decoded.payload.sub; 42 | authToken.issuedAt = decoded.payload.iat; 43 | authToken.expiresAt = decoded.payload.exp; 44 | authToken.clientDomain = decoded.payload.client_domain; 45 | authToken.token = str; 46 | return authToken; 47 | }; 48 | } 49 | 50 | export type ChallengeParams = { 51 | accountKp: AccountKeypair; 52 | memoId?: string; 53 | clientDomain?: string; 54 | authHeaderSigner?: AuthHeaderSigner; 55 | }; 56 | 57 | export type XdrEncodedTransaction = string; 58 | export type NetworkPassphrase = string; 59 | 60 | export type ChallengeResponse = { 61 | transaction: XdrEncodedTransaction; 62 | network_passphrase: NetworkPassphrase; 63 | }; 64 | 65 | export type SignParams = { 66 | accountKp: AccountKeypair; 67 | challengeResponse: ChallengeResponse; 68 | walletSigner: WalletSigner; 69 | }; 70 | 71 | export type SignWithClientAccountParams = { 72 | transaction: Transaction; 73 | accountKp: AccountKeypair; 74 | }; 75 | 76 | export type SignWithDomainAccountParams = { 77 | transactionXDR: XdrEncodedTransaction; 78 | networkPassphrase: NetworkPassphrase; 79 | accountKp: AccountKeypair; 80 | }; 81 | 82 | export type HttpHeaders = { 83 | [key: string]: string; 84 | }; 85 | 86 | export type SignChallengeTxnParams = { 87 | accountKp: SigningKeypair; 88 | challengeTx: string; 89 | networkPassphrase: string; 90 | anchorDomain: string; 91 | }; 92 | 93 | export type SignChallengeTxnResponse = { 94 | transaction: XdrEncodedTransaction; 95 | networkPassphrase: NetworkPassphrase; 96 | }; 97 | 98 | export type AuthHeaderClaims = { 99 | account: string; 100 | home_domain: string; 101 | web_auth_endpoint: string; 102 | memo?: string; 103 | client_domain?: string; 104 | exp?: number; 105 | iat?: number; 106 | }; 107 | 108 | export type AuthHeaderCreateTokenParams = { 109 | claims: AuthHeaderClaims; 110 | clientDomain?: string; 111 | issuer?: AccountKeypair; 112 | }; 113 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Types/horizon.ts: -------------------------------------------------------------------------------- 1 | import { Memo, Horizon, Transaction } from "@stellar/stellar-sdk"; 2 | import { AccountKeypair } from "../Horizon/Account"; 3 | import { SponsoringBuilder, TransactionBuilder } from "../Horizon"; 4 | import { StellarAssetId } from "../Asset"; 5 | 6 | export enum NETWORK_URLS { 7 | PUBLIC = "https://horizon.stellar.org", 8 | TESTNET = "https://horizon-testnet.stellar.org", 9 | } 10 | 11 | export type TransactionParams = { 12 | sourceAddress: AccountKeypair; 13 | baseFee?: number; 14 | memo?: Memo; 15 | timebounds?: Horizon.Server.Timebounds | number; 16 | }; 17 | 18 | export type FeeBumpTransactionParams = { 19 | feeAddress: AccountKeypair; 20 | transaction: Transaction; 21 | baseFee?: number; 22 | }; 23 | 24 | export type SubmitWithFeeIncreaseParams = { 25 | sourceAddress: AccountKeypair; 26 | timeout: number; 27 | baseFeeIncrease: number; 28 | buildingFunction: (builder: TransactionBuilder) => TransactionBuilder; 29 | signerFunction?: (transaction: Transaction) => Transaction; 30 | baseFee?: number; 31 | memo?: Memo; 32 | maxFee?: number; 33 | }; 34 | 35 | export const HORIZON_LIMIT_MAX = 200; 36 | export const HORIZON_LIMIT_DEFAULT = 10; 37 | 38 | export enum HORIZON_ORDER { 39 | ASC = "asc", 40 | DESC = "desc", 41 | } 42 | 43 | export type PathPayParams = { 44 | destinationAddress: string; 45 | sendAsset: StellarAssetId; 46 | destAsset: StellarAssetId; 47 | sendAmount?: string; 48 | destAmount?: string; 49 | destMin?: string; 50 | sendMax?: string; 51 | }; 52 | 53 | export type CommonBuilder = TransactionBuilder | SponsoringBuilder; 54 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Types/index.ts: -------------------------------------------------------------------------------- 1 | import { RawAxiosRequestHeaders } from "axios"; 2 | import { Networks } from "@stellar/stellar-sdk"; 3 | import { ApplicationConfiguration, StellarConfiguration } from "../"; 4 | import { RecoveryServerMap } from "./recovery"; 5 | 6 | // Export types from root walletSdk/index.ts 7 | export type WalletParams = { 8 | stellarConfiguration: StellarConfiguration; 9 | applicationConfiguration?: ApplicationConfiguration; 10 | language?: string; 11 | }; 12 | 13 | export type WalletAnchor = { 14 | homeDomain: string; 15 | allowHttp?: boolean; 16 | language?: string; 17 | }; 18 | 19 | export type WalletRecoveryServers = { 20 | servers: RecoveryServerMap; 21 | }; 22 | 23 | export type ConfigParams = { 24 | stellarConfiguration: StellarConfiguration; 25 | applicationConfiguration?: ApplicationConfiguration; 26 | }; 27 | 28 | export type StellarConfigurationParams = { 29 | network: Networks; 30 | horizonUrl: string; 31 | baseFee?: number; 32 | defaultTimeout?: number; 33 | }; 34 | 35 | export type AxiosErrorData = { 36 | status?: number; 37 | statusText?: string; 38 | // eslint-disable-next-line 39 | responseData?: any; 40 | headers?: RawAxiosRequestHeaders; 41 | }; 42 | 43 | // Export all other types from walletSdk/Types.ts 44 | export * from "./anchor"; 45 | export * from "./auth"; 46 | export * from "./horizon"; 47 | export * from "./recovery"; 48 | export * from "./sep6"; 49 | export * from "./sep7"; 50 | export * from "./sep12"; 51 | export * from "./sep24"; 52 | export * from "./sep38"; 53 | export * from "./sep43"; 54 | export * from "./utils"; 55 | export * from "./watcher"; 56 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Types/recovery.ts: -------------------------------------------------------------------------------- 1 | import { Transaction } from "@stellar/stellar-sdk"; 2 | 3 | import { WalletSigner } from "../Auth"; 4 | import { AccountKeypair, PublicKeypair } from "../Horizon"; 5 | import { AuthToken } from "./auth"; 6 | import { CommonBuilder } from "./horizon"; 7 | 8 | /** 9 | * Configuration for recoverable wallet 10 | * @param accountAddress Stellar address of the account that is registering 11 | * @param deviceAddress Stellar address of the device that is added as a primary signer. It will 12 | * replace the master key of [accountAddress] 13 | * @param accountThreshold Low, medium, and high thresholds to set on the account 14 | * @param accountIdentity A list of account identities to be registered with the recovery servers 15 | * @param signerWeight Signer weight of the device and recovery keys to set 16 | * @param sponsorAddress optional Stellar address of the account sponsoring this transaction 17 | */ 18 | export type RecoverableWalletConfig = { 19 | accountAddress: AccountKeypair; 20 | deviceAddress: AccountKeypair; 21 | accountThreshold: AccountThreshold; 22 | accountIdentity: RecoveryIdentityMap; 23 | signerWeight: SignerWeight; 24 | sponsorAddress?: AccountKeypair; 25 | builderExtra?: (builder: CommonBuilder) => CommonBuilder; 26 | }; 27 | 28 | export type RecoverableWallet = { 29 | transaction: Transaction; 30 | signers: string[]; 31 | }; 32 | 33 | export type AccountSigner = { 34 | address: AccountKeypair; 35 | weight: number; 36 | }; 37 | 38 | /** 39 | * Account weights threshold 40 | * @param low Low threshold weight 41 | * @param medium Medium threshold weight 42 | * @param high High threshold weight 43 | */ 44 | export type AccountThreshold = { 45 | low: number; 46 | medium: number; 47 | high: number; 48 | }; 49 | 50 | export type SignerWeight = { 51 | device: number; 52 | recoveryServer: number; 53 | }; 54 | 55 | /** 56 | * Recovery server configuration 57 | * @property {string} endpoint - Main endpoint (root domain) of SEP-30 recovery server. E.g. `https://testanchor.stellar.org` 58 | * @property {string} authEndpoint - SEP-10 auth endpoint to be used. Should be in the format ``. E.g. `https://testanchor.stellar.org/auth` 59 | * @property {string} homeDomain - SEP-10 home domain. E.g. `testanchor.stellar.org` 60 | * @property {WalletSigner} [walletSigner] - WalletSigner used to sign authentication 61 | */ 62 | export type RecoveryServer = { 63 | endpoint: string; 64 | authEndpoint: string; 65 | homeDomain: string; 66 | walletSigner?: WalletSigner; 67 | clientDomain?: string; 68 | }; 69 | 70 | export type RecoveryServerKey = string; 71 | 72 | export type RecoveryServerSigning = { 73 | signerAddress: string; 74 | authToken: AuthToken; 75 | }; 76 | 77 | export type RecoveryServerSigningMap = { 78 | [key: RecoveryServerKey]: RecoveryServerSigning; 79 | }; 80 | 81 | export type RecoveryServerMap = { 82 | [key: RecoveryServerKey]: RecoveryServer; 83 | }; 84 | 85 | export type RecoveryAuthMap = { 86 | [key: RecoveryServerKey]: AuthToken; 87 | }; 88 | 89 | /** 90 | * The role of the identity. This value is not used by the server and is stored and echoed back in 91 | * responses as a way for a client to know conceptually who each identity represents 92 | */ 93 | export enum RecoveryRole { 94 | OWNER = "owner", 95 | SENDER = "sender", 96 | RECEIVER = "receiver", 97 | } 98 | 99 | export enum RecoveryType { 100 | STELLAR_ADDRESS = "stellar_address", 101 | PHONE_NUMBER = "phone_number", 102 | EMAIL = "email", 103 | } 104 | 105 | export type RecoveryAccountAuthMethod = { 106 | type: RecoveryType; 107 | value: string; 108 | }; 109 | 110 | export type RecoveryAccountIdentity = { 111 | role: RecoveryRole; 112 | authMethods: RecoveryAccountAuthMethod[]; 113 | }; 114 | 115 | export type RecoveryIdentityMap = { 116 | [key: RecoveryServerKey]: RecoveryAccountIdentity[]; 117 | }; 118 | 119 | export type RecoveryAccountRole = { 120 | role: RecoveryRole; 121 | authenticated?: boolean; 122 | }; 123 | 124 | export type RecoveryAccountSigner = { 125 | key: string; 126 | }; 127 | 128 | export type RecoveryAccount = { 129 | address: string; 130 | identities: RecoveryAccountRole[]; 131 | signers: RecoveryAccountSigner[]; 132 | }; 133 | 134 | export type RecoverableIdentity = { 135 | role: string; 136 | authenticated?: boolean; 137 | }; 138 | 139 | export type RecoverableSigner = { 140 | key: PublicKeypair; 141 | addedAt?: Date; 142 | }; 143 | 144 | export type RecoverableAccountInfo = { 145 | address: PublicKeypair; 146 | identities: RecoverableIdentity[]; 147 | signers: RecoverableSigner[]; 148 | }; 149 | 150 | export type RecoveryAccountInfoMap = { 151 | [key: RecoveryServerKey]: RecoverableAccountInfo; 152 | }; 153 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Types/sep12.ts: -------------------------------------------------------------------------------- 1 | export type CustomerInfoMap = { 2 | [key: string]: string; 3 | }; 4 | 5 | export type CustomerBinaryInfoMap = { 6 | [key: string]: Buffer; 7 | }; 8 | 9 | export enum Sep12Status { 10 | ACCEPTED = "ACCEPTED", 11 | PROCESSING = "PROCESSING", 12 | NEEDS_INFO = "NEEDS_INFO", 13 | REJECTED = "REJECTED", 14 | VERIFICATION_REQUIRED = "VERIFICATION_REQUIRED", 15 | } 16 | 17 | export enum Sep12Type { 18 | string = "string", 19 | binary = "binary", 20 | number = "number", 21 | date = "date", 22 | } 23 | 24 | export type Field = { 25 | type: Sep12Type; 26 | description: string; 27 | choices?: Array; 28 | optional?: boolean; 29 | }; 30 | 31 | export type ProvidedField = { 32 | type: Sep12Type; 33 | description: string; 34 | choices?: Array; 35 | optional?: boolean; 36 | status?: Sep12Status; 37 | error?: string; 38 | }; 39 | 40 | export type GetCustomerParams = { 41 | id?: string; 42 | type?: string; 43 | memo?: string; 44 | lang?: string; 45 | transactionId?: string; 46 | }; 47 | 48 | export type GetCustomerResponse = { 49 | id?: string; 50 | status: Sep12Status; 51 | fields?: { [key: string]: Field }; 52 | provided_fields?: { [key: string]: ProvidedField }; 53 | message?: string; 54 | }; 55 | 56 | export type AddCustomerParams = { 57 | sep9Info?: CustomerInfoMap; 58 | sep9BinaryInfo?: CustomerBinaryInfoMap; 59 | id?: string; 60 | memo?: string; 61 | type?: string; 62 | transactionId?: string; 63 | }; 64 | 65 | export type AddCustomerResponse = { 66 | id: string; 67 | }; 68 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Types/sep24.ts: -------------------------------------------------------------------------------- 1 | import { Memo } from "@stellar/stellar-sdk"; 2 | 3 | import { AuthToken } from "./auth"; 4 | 5 | export interface Sep24Info { 6 | deposit: Sep24AssetInfoMap; 7 | withdraw: Sep24AssetInfoMap; 8 | fee: { enabled: boolean }; 9 | features: { account_creation: boolean; claimable_balances: boolean }; 10 | } 11 | 12 | export interface Sep24AssetInfoMap { 13 | [asset_code: string]: Sep24AssetInfo; 14 | } 15 | 16 | export interface Sep24AssetInfo { 17 | enabled: boolean; 18 | min_amount: number; 19 | max_amount: number; 20 | fee_fixed: number; 21 | fee_percent: number; 22 | } 23 | 24 | export enum FLOW_TYPE { 25 | DEPOSIT = "deposit", 26 | WITHDRAW = "withdraw", 27 | } 28 | 29 | // Extra fields should be sent as snake_case keys 30 | // since the SEP api spec uses that format for all params 31 | export type ExtraFields = { 32 | [api_key: string]: string; 33 | }; 34 | 35 | export type Sep24PostParams = { 36 | authToken: AuthToken; 37 | assetCode: string; 38 | lang?: string; 39 | extraFields?: ExtraFields; 40 | destinationMemo?: Memo; 41 | destinationAccount?: string; 42 | withdrawalAccount?: string; 43 | account?: string; 44 | }; 45 | 46 | export enum Sep24ResponseType { 47 | authentication_required = "authentication_required", 48 | interactive_customer_info_needed = "interactive_customer_info_needed", 49 | error = "error", 50 | } 51 | 52 | export interface Sep24PostResponse { 53 | type: 54 | | Sep24ResponseType.authentication_required 55 | | Sep24ResponseType.interactive_customer_info_needed 56 | | Sep24ResponseType.error; 57 | id?: string; 58 | url?: string; 59 | error?: string; 60 | } 61 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Types/sep38.ts: -------------------------------------------------------------------------------- 1 | import { AxiosInstance } from "axios"; 2 | import { Anchor } from "../Anchor"; 3 | import { AuthToken } from "./auth"; 4 | 5 | export interface Sep38Info { 6 | assets: Array; 7 | } 8 | 9 | export interface Sep38AssetInfo { 10 | asset: string; 11 | sell_delivery_methods?: Array; 12 | buy_delivery_methods?: Array; 13 | country_codes?: Array; 14 | } 15 | 16 | export interface Sep38DeliveryMethod { 17 | name: string; 18 | description: string; 19 | } 20 | 21 | export type Sep38Params = { 22 | anchor: Anchor; 23 | httpClient: AxiosInstance; 24 | authToken?: AuthToken; 25 | }; 26 | 27 | export interface Sep38PricesParams { 28 | sellAsset: string; 29 | sellAmount: string; 30 | sellDeliveryMethod?: string; 31 | buyDeliveryMethod?: string; 32 | countryCode?: string; 33 | } 34 | 35 | export interface Sep38PricesResponse { 36 | buy_assets: Array; 37 | } 38 | 39 | export interface Sep38BuyAsset { 40 | asset: string; 41 | price: string; 42 | decimals: number; 43 | } 44 | 45 | export interface Sep38PriceParams { 46 | sellAsset: string; 47 | buyAsset: string; 48 | sellAmount?: string; 49 | buyAmount?: string; 50 | context: Sep38PriceContext; 51 | sellDeliveryMethod?: string; 52 | buyDeliveryMethod?: string; 53 | countryCode?: string; 54 | } 55 | 56 | export enum Sep38PriceContext { 57 | SEP6 = "sep6", 58 | SEP24 = "sep24", 59 | SEP31 = "sep31", 60 | } 61 | 62 | export interface Sep38PriceResponse { 63 | total_price: string; 64 | price: string; 65 | sell_amount: string; 66 | buy_amount: string; 67 | fee: { 68 | total: string; 69 | asset: string; 70 | details?: Array; 71 | }; 72 | } 73 | 74 | export interface Sep38FeeDetails { 75 | name: string; 76 | description?: string; 77 | amount: string; 78 | } 79 | 80 | export interface Sep38PostQuoteParams { 81 | sell_asset: string; 82 | buy_asset: string; 83 | sell_amount: string; 84 | buy_amount: string; 85 | context: Sep38PriceContext; 86 | expire_after?: string; 87 | sell_delivery_method?: string; 88 | buy_delivery_method?: string; 89 | country_code?: string; 90 | } 91 | 92 | export interface Sep38PostQuoteResponse { 93 | id: string; 94 | expires_at: string; 95 | total_price: string; 96 | price: string; 97 | sell_asset: string; 98 | sell_amount: string; 99 | buy_asset: string; 100 | buy_amount: string; 101 | fee: { 102 | total: string; 103 | asset: string; 104 | details?: Array; 105 | }; 106 | } 107 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Types/sep43.ts: -------------------------------------------------------------------------------- 1 | interface Error { 2 | message: string; // general description message returned to the client app 3 | code: number; // unique error code 4 | ext?: Array; // optional extended details 5 | } 6 | 7 | export interface Sep43Interface { 8 | /** 9 | * Function used to request the public key from the active account 10 | * 11 | * @return Promise<{ address: string } & { error?: Error }> 12 | */ 13 | getAddress(): Promise<{ address: string } & { error?: Error }>; 14 | 15 | /** 16 | * A function to request a wallet to sign a built transaction in its XDR mode 17 | * 18 | * @param xdr - A Transaction or a FeeBumpTransaction 19 | * @param opts - Options compatible with https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0043.md#signtransaction 20 | * @param opts.networkPassphrase - The Stellar network to use when signing 21 | * @param opts.address - The public key of the account that should be used to sign 22 | * @param opts.path - This options is added for special cases like Hardware wallets 23 | * 24 | * @return Promise<{ signedTxXdr: string; signerAddress: string } & { error?: Error }> 25 | */ 26 | signTransaction( 27 | xdr: string, 28 | opts?: { 29 | networkPassphrase?: string; 30 | address?: string; 31 | path?: string; 32 | submit?: boolean; 33 | submitUrl?: string; 34 | }, 35 | ): Promise< 36 | { signedTxXdr: string; signerAddress: string } & { error?: Error } 37 | >; 38 | 39 | /** 40 | * A function to request a wallet to sign an AuthEntry XDR. 41 | * 42 | * @param authEntry - An XDR string version of `HashIdPreimageSorobanAuthorization` 43 | * @param opts - Options compatible with https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0043.md#signauthentry 44 | * @param opts.networkPassphrase - The Stellar network to use when signing 45 | * @param opts.address - The public key of the account that should be used to sign 46 | * @param opts.path - This options is added for special cases like Hardware wallets 47 | * 48 | * @return - Promise<{ signedAuthEntry: string; signerAddress: string } & { error?: Error }> 49 | */ 50 | signAuthEntry( 51 | authEntry: string, 52 | opts?: { 53 | networkPassphrase?: string; 54 | address?: string; 55 | path?: string; 56 | }, 57 | ): Promise< 58 | { signedAuthEntry: string; signerAddress: string } & { error?: Error } 59 | >; 60 | 61 | /** 62 | * A function to request a wallet to sign an AuthEntry XDR. 63 | * 64 | * @param message - An arbitrary string rather than a transaction or auth entry 65 | * @param opts - Options compatible with https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0043.md#signmessage 66 | * @param opts.networkPassphrase - The Stellar network to use when signing 67 | * @param opts.address - The public key of the account that should be used to sign 68 | * @param opts.path - This options is added for special cases like Hardware wallets 69 | * 70 | * @return - Promise<{ signedMessage: string; signerAddress: string } & { error?: Error }> 71 | */ 72 | signMessage( 73 | message: string, 74 | opts?: { 75 | networkPassphrase?: string; 76 | address?: string; 77 | path?: string; 78 | }, 79 | ): Promise< 80 | { signedMessage: string; signerAddress: string } & { error?: Error } 81 | >; 82 | 83 | /** 84 | * A function to request the current selected network in the wallet. This comes 85 | * handy when you are dealing with a wallet that doesn't allow you to specify which network to use (For example Lobstr and Rabet) 86 | * 87 | * @return - Promise<{ network: string; networkPassphrase: string } & { error?: Error }> 88 | */ 89 | getNetwork(): Promise< 90 | { network: string; networkPassphrase: string } & { error?: Error } 91 | >; 92 | } 93 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Types/sep6.ts: -------------------------------------------------------------------------------- 1 | import { AxiosInstance } from "axios"; 2 | import { Anchor } from "../Anchor"; 3 | 4 | export interface Sep6EndpointInfo { 5 | enabled: boolean; 6 | authentication_required?: boolean; 7 | description?: string; 8 | } 9 | 10 | export interface Sep6DepositInfo { 11 | enabled: boolean; 12 | authentication_required?: boolean; 13 | fee_fixed?: number; 14 | fee_percent?: number; 15 | min_amount?: number; 16 | max_amount?: number; 17 | fields?: { 18 | [key: string]: { 19 | description: string; 20 | optional?: boolean; 21 | choices?: string[]; 22 | }; 23 | }; 24 | } 25 | 26 | export interface Sep6WithdrawInfo { 27 | enabled: boolean; 28 | authentication_required?: boolean; 29 | fee_fixed?: number; 30 | fee_percent?: number; 31 | min_amount?: number; 32 | max_amount?: number; 33 | types?: { 34 | [key: string]: { 35 | fields?: { 36 | [key: string]: { 37 | description: string; 38 | optional?: boolean; 39 | choices?: string[]; 40 | }; 41 | }; 42 | }; 43 | }; 44 | } 45 | 46 | export interface Sep6Info { 47 | deposit: { [key: string]: Sep6DepositInfo }; 48 | "deposit-exchange": { [key: string]: Sep6DepositInfo }; 49 | withdraw: { [key: string]: Sep6WithdrawInfo }; 50 | "withdraw-exchange": { [key: string]: Sep6WithdrawInfo }; 51 | fee: { 52 | enabled: boolean; 53 | description: string; 54 | }; 55 | transactions: Sep6EndpointInfo; 56 | transaction: Sep6EndpointInfo; 57 | features: { 58 | account_creation: boolean; 59 | claimable_balances: boolean; 60 | }; 61 | } 62 | 63 | export type Sep6Params = { 64 | anchor: Anchor; 65 | httpClient: AxiosInstance; 66 | }; 67 | 68 | export interface Sep6DepositParams { 69 | asset_code: string; 70 | account: string; 71 | memo_type?: string; 72 | memo?: string; 73 | email_address?: string; 74 | type?: string; 75 | lang?: string; 76 | on_change_callback?: string; 77 | amount?: string; 78 | country_code?: string; 79 | claimable_balance_supported?: string; 80 | customer_id?: string; 81 | } 82 | 83 | export interface Sep6WithdrawParams { 84 | asset_code: string; 85 | type: string; 86 | dest?: string; 87 | dest_extra?: string; 88 | account?: string; 89 | memo?: string; 90 | lang?: string; 91 | on_change_callback?: string; 92 | amount?: string; 93 | country_code?: string; 94 | refund_memo?: string; 95 | refund_memo_type?: string; 96 | customer_id?: string; 97 | } 98 | 99 | export type Sep6DepositResponse = 100 | | Sep6DepositSuccess 101 | | Sep6MissingKYC 102 | | Sep6Pending; 103 | 104 | export interface Sep6DepositSuccess { 105 | how?: string; 106 | instructions?: { 107 | [key: string]: { 108 | value: string; 109 | description: string; 110 | }; 111 | }; 112 | id?: string; 113 | eta?: number; 114 | min_amoun?: number; 115 | max_amount?: number; 116 | fee_fixed?: number; 117 | fee_percent?: number; 118 | extra_info?: { message?: string }; 119 | } 120 | 121 | export interface Sep6MissingKYC { 122 | type: string; 123 | fields: Array; 124 | } 125 | 126 | export interface Sep6Pending { 127 | type: string; 128 | status: string; 129 | more_info_url?: string; 130 | eta?: number; 131 | } 132 | 133 | export type Sep6WithdrawResponse = 134 | | Sep6WithdrawSuccess 135 | | Sep6MissingKYC 136 | | Sep6Pending; 137 | 138 | export interface Sep6WithdrawSuccess { 139 | account_id?: string; 140 | memo_type?: string; 141 | memo?: string; 142 | id?: string; 143 | eta?: number; 144 | min_amount?: number; 145 | max_amount?: number; 146 | fee_fixed?: number; 147 | fee_percent?: number; 148 | extra_info?: { message?: string }; 149 | } 150 | 151 | export interface Sep6ExchangeParams { 152 | destination_asset: string; 153 | source_asset: string; 154 | amount: string; 155 | account?: string; 156 | quote_id?: string; 157 | memo_type?: string; 158 | memo?: string; 159 | email_address?: string; 160 | type?: string; 161 | lang?: string; 162 | on_change_callback?: string; 163 | country_code?: string; 164 | claimable_balance_supported?: string; 165 | customer_id?: string; 166 | refund_memo?: string; 167 | refund_memo_type?: string; 168 | } 169 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Types/sep7.ts: -------------------------------------------------------------------------------- 1 | export const WEB_STELLAR_SCHEME = "web+stellar:"; 2 | 3 | export enum Sep7OperationType { 4 | tx = "tx", 5 | pay = "pay", 6 | } 7 | 8 | export const URI_MSG_MAX_LENGTH = 300; 9 | 10 | export type Sep7Replacement = { 11 | id: string; 12 | path: string; 13 | hint: string; 14 | }; 15 | 16 | export type IsValidSep7UriResult = { 17 | result: boolean; 18 | reason?: string; 19 | }; 20 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Types/utils.ts: -------------------------------------------------------------------------------- 1 | export type TomlInfo = { 2 | version: string; 3 | networkPassphrase: string; 4 | federationServer: string; 5 | authServer: string; 6 | transferServer: string; 7 | transferServerSep24: string; 8 | kycServer: string; 9 | webAuthEndpoint: string; 10 | signingKey: string; 11 | horizonUrl: string; 12 | accounts: Array; 13 | uriRequestSigningKey: string; 14 | directPaymentServer: string; 15 | anchorQuoteServer: string; 16 | documentation: { 17 | orgName: string; 18 | orgDba: string; 19 | orgUrl: string; 20 | orgLogo: string; 21 | orgDescription: string; 22 | orgPhysicalAddress: string; 23 | orgPhysicalAddressAttestation: string; 24 | orgPhoneNumber: string; 25 | orgPhoneNumberAttestation: string; 26 | orgKeybase: string; 27 | orgTwitter: string; 28 | orgGithub: string; 29 | orgOfficialEmail: string; 30 | orgSupportEmail: string; 31 | orgLicensingAuthority: string; 32 | orgLicenseType: string; 33 | orgLicenseNumber: string; 34 | }; 35 | principals: Array<{ 36 | name: string; 37 | email: string; 38 | keybase: string; 39 | telegram: string; 40 | twitter: string; 41 | github: string; 42 | idPhotoHash: string; 43 | verificationPhotoHash: string; 44 | }>; 45 | currencies: Array<{ 46 | code: string; 47 | codeTemplate: string; 48 | issuer: string; 49 | status: string; 50 | displayDecimals: number; 51 | name: string; 52 | desc: string; 53 | conditions: string; 54 | image: string; 55 | fixedNumber: number; 56 | maxNumber: number; 57 | isUnlimited: boolean; 58 | isAssetAnchored: boolean; 59 | anchorAssetType: string; 60 | anchorAsset: string; 61 | attestationOfReserve: string; 62 | redemptionInstructions: string; 63 | collateralAddresses: Array; 64 | collateralAddressMessages: Array; 65 | collateralAddressSignatures: Array; 66 | regulated: boolean; 67 | approvalServer: string; 68 | approvalCriteria: string; 69 | }>; 70 | validators: Array<{ 71 | alias: string; 72 | displayName: string; 73 | publicKey: string; 74 | host: string; 75 | history: string; 76 | }>; 77 | }; 78 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Types/watcher.ts: -------------------------------------------------------------------------------- 1 | import { AnchorTransaction } from "./anchor"; 2 | import { AuthToken } from "./auth"; 3 | 4 | export type WatchTransactionsParams = { 5 | authToken: AuthToken; 6 | assetCode: string; 7 | onMessage: (transaction: AnchorTransaction) => void; 8 | onError: (error: AnchorTransaction | Error) => void; 9 | watchlist?: string[]; 10 | timeout?: number; 11 | isRetry?: boolean; 12 | lang?: string; 13 | kind?: string; 14 | noOlderThan?: string; 15 | }; 16 | 17 | export type WatchTransactionParams = { 18 | authToken: AuthToken; 19 | assetCode: string; 20 | id: string; 21 | onMessage: (transaction: AnchorTransaction) => void; 22 | onSuccess: (transaction: AnchorTransaction) => void; 23 | onError: (error: AnchorTransaction | Error) => void; 24 | timeout?: number; 25 | isRetry?: boolean; 26 | lang?: string; 27 | }; 28 | 29 | export type WatcherRefreshFunction = () => void; 30 | export type WatcherStopFunction = () => void; 31 | 32 | export interface WatcherResponse { 33 | refresh: WatcherRefreshFunction; 34 | stop: WatcherStopFunction; 35 | } 36 | 37 | export enum WatcherSepType { 38 | SEP6 = "SEP6", 39 | SEP24 = "SEP24", 40 | } 41 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Uri/Sep7Pay.ts: -------------------------------------------------------------------------------- 1 | import { MemoType } from "@stellar/stellar-sdk"; 2 | import { Sep7Base } from "../Uri"; 3 | import { Sep7OperationType, WEB_STELLAR_SCHEME } from "../Types"; 4 | 5 | /** 6 | * The Sep-7 'pay' operation represents a request to pay a specific address 7 | * with a specific asset, regardless of the source asset used by the payer. 8 | * 9 | * @see https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0007.md#operation-pay 10 | */ 11 | export class Sep7Pay extends Sep7Base { 12 | /** 13 | * Creates a Sep7Pay instance with given destination. 14 | * 15 | * @param {string} destination a valid Stellar address to receive the payment. 16 | * 17 | * @returns {Sep7Pay} the Sep7Pay instance. 18 | */ 19 | static forDestination(destination: string): Sep7Pay { 20 | const uri = new Sep7Pay(); 21 | uri.destination = destination; 22 | return uri; 23 | } 24 | 25 | /** 26 | * Creates a new instance of the Sep7Pay class. 27 | * 28 | * @constructor 29 | * @param {URL | string} [uri] - uri to initialize the Sep7 instance. 30 | */ 31 | constructor(uri?: URL | string) { 32 | super(uri ?? new URL(`${WEB_STELLAR_SCHEME}${Sep7OperationType.pay}`)); 33 | } 34 | 35 | /** 36 | * Returns a deep clone of this instance. 37 | * 38 | * @returns {Sep7Pay} a deep clone of this Sep7Pay instance. 39 | */ 40 | clone(): Sep7Pay { 41 | return new Sep7Pay(this.uri); 42 | } 43 | 44 | /** 45 | * Gets the destination of the payment request, which should be a valid 46 | * Stellar address. 47 | * 48 | * @returns {string | undefined} the 'destination' uri param if present. 49 | */ 50 | get destination(): string | undefined { 51 | return this.getParam("destination"); 52 | } 53 | 54 | /** 55 | * Sets the destination of the payment request, which should be a valid 56 | * Stellar address. 57 | * 58 | * Deletes the uri 'destination' param if set as 'undefined'. 59 | * 60 | * @param {string | undefined} destination the uri 'destination' param to be set. 61 | */ 62 | set destination(destination: string | undefined) { 63 | this.setParam("destination", destination); 64 | } 65 | 66 | /** 67 | * Gets the amount that destination should receive. 68 | * 69 | * @returns {string | undefined} the 'amount' uri param if present. 70 | */ 71 | get amount(): string | undefined { 72 | return this.getParam("amount"); 73 | } 74 | 75 | /** 76 | * Sets the amount that destination should receive. 77 | * 78 | * Deletes the uri 'amount' param if set as 'undefined'. 79 | * 80 | * @param {string | undefined} amount the uri 'amount' param to be set. 81 | */ 82 | set amount(amount: string | undefined) { 83 | this.setParam("amount", amount); 84 | } 85 | 86 | /** 87 | * Gets the code from the asset that destination should receive. 88 | * 89 | * @returns {string | undefined} the 'asset_code' uri param if present. 90 | */ 91 | get assetCode(): string | undefined { 92 | return this.getParam("asset_code"); 93 | } 94 | 95 | /** 96 | * Sets the code from the asset that destination should receive. 97 | * 98 | * Deletes the uri 'asset_code' param if set as 'undefined'. 99 | * 100 | * @param {string | undefined} assetCode the uri 'asset_code' param to be set. 101 | */ 102 | set assetCode(assetCode: string | undefined) { 103 | this.setParam("asset_code", assetCode); 104 | } 105 | 106 | /** 107 | * Gets the account ID of asset issuer the destination should receive. 108 | * 109 | * @returns {string | undefined} the 'asset_issuer' uri param if present. 110 | */ 111 | get assetIssuer(): string | undefined { 112 | return this.getParam("asset_issuer"); 113 | } 114 | 115 | /** 116 | * Sets the account ID of asset issuer the destination should receive. 117 | * 118 | * Deletes the uri 'asset_issuer' param if set as 'undefined'. 119 | * 120 | * @param {string | undefined} assetIssuer the uri 'asset_issuer' param to be set. 121 | */ 122 | set assetIssuer(assetIssuer: string | undefined) { 123 | this.setParam("asset_issuer", assetIssuer); 124 | } 125 | 126 | /** 127 | * Gets the memo to be included in the payment / path payment. 128 | * Memos of type MEMO_HASH and MEMO_RETURN should be base64-decoded 129 | * after returned from this function. 130 | * 131 | * @returns {string | undefined} the 'memo' uri param if present. 132 | */ 133 | get memo(): string | undefined { 134 | return this.getParam("memo"); 135 | } 136 | 137 | /** 138 | * Sets the memo to be included in the payment / path payment. 139 | * Memos of type MEMO_HASH and MEMO_RETURN should be base64-encoded 140 | * prior to being passed on this function. 141 | * 142 | * Deletes the uri 'memo' param if set as 'undefined'. 143 | * 144 | * @param {string | undefined} memo the uri 'memo' param to be set. 145 | */ 146 | set memo(memo: string | undefined) { 147 | this.setParam("memo", memo); 148 | } 149 | 150 | /** 151 | * Gets the type of the memo. 152 | * 153 | * @returns {MemoType | undefined} the 'memo_type' uri param if present. 154 | */ 155 | get memoType(): MemoType | undefined { 156 | return this.getParam("memo_type") as MemoType | undefined; 157 | } 158 | 159 | /** 160 | * Sets the type of the memo. 161 | * 162 | * Deletes the uri 'memo_type' param if set as 'undefined'. 163 | * 164 | * @param {MemoType | undefined} memoType the uri 'memo_type' param to be set. 165 | */ 166 | set memoType(memoType: MemoType | undefined) { 167 | this.setParam("memo_type", memoType); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Uri/index.ts: -------------------------------------------------------------------------------- 1 | export { Sep7Base } from "./Sep7Base"; 2 | export { Sep7Pay } from "./Sep7Pay"; 3 | export { Sep7Tx } from "./Sep7Tx"; 4 | export { 5 | isValidSep7Uri, 6 | parseSep7Uri, 7 | sep7ReplacementsFromString, 8 | sep7ReplacementsToString, 9 | } from "./sep7Parser"; 10 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Utils/camelToSnakeCase.ts: -------------------------------------------------------------------------------- 1 | export const camelToSnakeCaseKey = (key: string): string => 2 | key.replace( 3 | /[A-Z]/g, 4 | (upperCaseLetter) => `_${upperCaseLetter.toLowerCase()}`, 5 | ); 6 | 7 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 8 | export const camelToSnakeCaseObject = (obj: any): any => { 9 | const snakeCasedObj = {}; 10 | for (const key in obj) { 11 | if (obj.hasOwnProperty(key)) { 12 | const snakeCaseKey = camelToSnakeCaseKey(key); 13 | snakeCasedObj[snakeCaseKey] = obj[key]; 14 | } 15 | } 16 | return snakeCasedObj; 17 | }; 18 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Utils/extractAxiosErrorData.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { AxiosErrorData } from "../Types"; 3 | 4 | // Based on https://axios-http.com/docs/handling_errors 5 | 6 | export const extractAxiosErrorData = (error: Error): AxiosErrorData => { 7 | if (!axios.isAxiosError(error)) { 8 | return { responseData: JSON.stringify(error) }; 9 | } 10 | if (error.response) { 11 | return { 12 | status: error.response.status, 13 | statusText: error.response.statusText, 14 | responseData: error.response.data, 15 | headers: error.response.headers, 16 | }; 17 | } else if (error.request) { 18 | // The request was made but no response was received 19 | return { 20 | statusText: `No response received from request: ${JSON.stringify( 21 | error.request, 22 | )}`, 23 | }; 24 | } else { 25 | // Something happened in setting up the request that triggered an Error 26 | return { statusText: `Failed request with error: ${error.message}` }; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Utils/getResultCode.ts: -------------------------------------------------------------------------------- 1 | import get from "lodash/get"; 2 | 3 | export const getResultCode = (error: Error) => { 4 | return get(error, "response.data.extras.result_codes.transaction", ""); 5 | }; 6 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./camelToSnakeCase"; 2 | export * from "./extractAxiosErrorData"; 3 | export * from "./getResultCode"; 4 | export * from "./toml"; 5 | export * from "./url"; 6 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Utils/toml.ts: -------------------------------------------------------------------------------- 1 | import { StellarToml } from "@stellar/stellar-sdk"; 2 | 3 | import { TomlInfo } from "../Types"; 4 | 5 | export const parseToml = (toml: StellarToml.Api.StellarToml): TomlInfo => { 6 | const tomlDocumentation = toml["DOCUMENTATION"]; 7 | const documentation = { 8 | orgName: tomlDocumentation["ORG_NAME"], 9 | orgDba: tomlDocumentation["ORG_DBA"], 10 | orgUrl: tomlDocumentation["ORG_URL"], 11 | orgLogo: tomlDocumentation["ORG_LOGO"], 12 | orgDescription: tomlDocumentation["ORG_DESCRIPTION"], 13 | orgPhysicalAddress: tomlDocumentation["ORG_PHYSICAL_ADDRESS"], 14 | orgPhysicalAddressAttestation: 15 | tomlDocumentation["ORG_PHYSICAL_ADDRESS_ATTESTATION"], 16 | orgPhoneNumber: tomlDocumentation["ORG_PHONE_NUMBER"], 17 | orgPhoneNumberAttestation: 18 | tomlDocumentation["ORG_PHONE_NUMBER_ATTESTATION"], 19 | orgKeybase: tomlDocumentation["ORG_KEYBASE"], 20 | orgTwitter: tomlDocumentation["ORG_TWITTER"], 21 | orgGithub: tomlDocumentation["ORG_GITHUB"], 22 | orgOfficialEmail: tomlDocumentation["ORG_OFFICIAL_EMAIL"], 23 | orgSupportEmail: tomlDocumentation["ORG_SUPPORT_EMAIL"], 24 | orgLicensingAuthority: tomlDocumentation["ORG_LICENSING_AUTHORITY"], 25 | orgLicenseType: tomlDocumentation["ORG_LICENSE_TYPE"], 26 | orgLicenseNumber: tomlDocumentation["ORG_LICENSE_NUMBER"], 27 | }; 28 | 29 | const tomlPrincipals = toml["PRINCIPALS"] || []; 30 | const principals = tomlPrincipals.map((tp) => { 31 | return { 32 | name: tp["name"], 33 | email: tp["email"], 34 | keybase: tp["keybase"], 35 | telegram: tp["telegram"], 36 | twitter: tp["twitter"], 37 | github: tp["github"], 38 | idPhotoHash: tp["id_photo_hash"], 39 | verificationPhotoHash: tp["verification_photo_hash"], 40 | }; 41 | }); 42 | 43 | const tomlCurrencies = toml["CURRENCIES"] || []; 44 | const currencies = tomlCurrencies.map((tc) => { 45 | return { 46 | code: tc["code"], 47 | codeTemplate: tc["code_template"], 48 | issuer: tc["issuer"], 49 | status: tc["status"], 50 | displayDecimals: tc["display_decimals"], 51 | name: tc["name"], 52 | desc: tc["desc"], 53 | conditions: tc["conditions"], 54 | image: tc["image"], 55 | fixedNumber: tc["fixed_number"], 56 | maxNumber: tc["max_number"], 57 | isUnlimited: tc["is_unlimited"], 58 | isAssetAnchored: tc["is_asset_anchored"], 59 | anchorAssetType: tc["anchor_asset_type"], 60 | anchorAsset: tc["anchor_asset"], 61 | attestationOfReserve: tc["attestation_of_reserve"], 62 | redemptionInstructions: tc["redemption_instructions"], 63 | collateralAddresses: tc["collateral_addresses"], 64 | collateralAddressMessages: tc["collateral_address_messages"], 65 | collateralAddressSignatures: tc["collateral_address_signatures"], 66 | regulated: tc["regulated"], 67 | approvalServer: tc["approval_server"], 68 | approvalCriteria: tc["approval_criteria"], 69 | }; 70 | }); 71 | 72 | const tomlValidators = toml["VALIDATORS"] || []; 73 | const validators = tomlValidators.map((tv) => { 74 | return { 75 | alias: tv["ALIAS"], 76 | displayName: tv["DISPLAY_NAME"], 77 | publicKey: tv["PUBLIC_KEY"], 78 | host: tv["HOST"], 79 | history: tv["HISTORY"], 80 | }; 81 | }); 82 | 83 | return { 84 | version: toml["VERSION"], 85 | networkPassphrase: toml["NETWORK_PASSPHRASE"], 86 | federationServer: toml["FEDERATION_SERVER"], 87 | authServer: toml["AUTH_SERVER"] as string, 88 | transferServer: toml["TRANSFER_SERVER"], 89 | transferServerSep24: toml["TRANSFER_SERVER_SEP0024"], 90 | kycServer: toml["KYC_SERVER"], 91 | webAuthEndpoint: toml["WEB_AUTH_ENDPOINT"], 92 | signingKey: toml["SIGNING_KEY"], 93 | horizonUrl: toml["HORIZON_URL"], 94 | accounts: toml["ACCOUNTS"], 95 | uriRequestSigningKey: toml["URI_REQUEST_SIGNING_KEY"], 96 | directPaymentServer: toml["DIRECT_PAYMENT_SERVER"], 97 | anchorQuoteServer: toml["ANCHOR_QUOTE_SERVER"], 98 | documentation, 99 | principals, 100 | currencies, 101 | validators, 102 | }; 103 | }; 104 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Utils/url.ts: -------------------------------------------------------------------------------- 1 | import { URL } from "url"; 2 | 3 | let Url; 4 | if (typeof window === "undefined") { 5 | Url = URL; 6 | } else { 7 | Url = window.URL; 8 | } 9 | 10 | export const getUrlDomain = (url: string) => { 11 | return new Url(url).host; 12 | }; 13 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/src/walletSdk/Watcher/getTransactions.ts: -------------------------------------------------------------------------------- 1 | import { AxiosInstance } from "axios"; 2 | import queryString from "query-string"; 3 | 4 | import { AuthToken } from "../Types"; 5 | import { 6 | ServerRequestFailedError, 7 | InvalidTransactionsResponseError, 8 | InvalidTransactionResponseError, 9 | } from "../Exceptions"; 10 | 11 | export const _getTransactionsForAsset = async ( 12 | authToken: AuthToken, 13 | params: { [key: string]: string | number }, 14 | endpoint: string, 15 | client: AxiosInstance, 16 | ): Promise => { 17 | try { 18 | const resp = await client.get( 19 | `${endpoint}/transactions?${queryString.stringify(params)}`, 20 | { 21 | headers: { 22 | Authorization: `Bearer ${authToken.token}`, 23 | }, 24 | }, 25 | ); 26 | 27 | const transactions: T[] = resp.data.transactions; 28 | 29 | if (!transactions || !Array.isArray(transactions)) { 30 | throw new InvalidTransactionsResponseError(transactions); 31 | } 32 | 33 | return transactions; 34 | } catch (e) { 35 | throw new ServerRequestFailedError(e); 36 | } 37 | }; 38 | 39 | export const _getTransactionBy = async ( 40 | authToken: AuthToken, 41 | params: { [key: string]: string | number }, 42 | endpoint: string, 43 | client: AxiosInstance, 44 | ): Promise => { 45 | try { 46 | const resp = await client.get( 47 | `${endpoint}/transaction?${queryString.stringify(params)}`, 48 | { 49 | headers: { 50 | Authorization: `Bearer ${authToken.token}`, 51 | }, 52 | }, 53 | ); 54 | 55 | const transaction: T = resp.data.transaction; 56 | 57 | if (!transaction || Object.keys(transaction).length === 0) { 58 | throw new InvalidTransactionResponseError(transaction); 59 | } 60 | 61 | return transaction; 62 | } catch (e) { 63 | throw new ServerRequestFailedError(e); 64 | } 65 | }; 66 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/test/account.test.ts: -------------------------------------------------------------------------------- 1 | import crypto from "crypto"; 2 | import { TransactionBuilder, Networks } from "@stellar/stellar-sdk"; 3 | 4 | import { Wallet } from "../src"; 5 | import { PublicKeypair } from "../src/walletSdk/Horizon/Account"; 6 | 7 | let wal; 8 | let account; 9 | describe("Account", () => { 10 | beforeEach(() => { 11 | wal = Wallet.TestNet(); 12 | account = wal.stellar().account(); 13 | }); 14 | it("should create keypair and sign", () => { 15 | const kp = account.createKeypair(); 16 | expect(kp.publicKey).toBeTruthy(); 17 | expect(kp.secretKey).toBeTruthy(); 18 | 19 | const tx = TransactionBuilder.fromXDR( 20 | "AAAAAgAAAADk/TqnRl6sFK79yasH46qlX/dFxQ8R023aHRxAkUmE8wAAAGQAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAADk/TqnRl6sFK79yasH46qlX/dFxQ8R023aHRxAkUmE8wAAAAAAAAAABfXhAAAAAAAAAAAA", 21 | Networks.TESTNET, 22 | ); 23 | kp.sign(tx); 24 | expect(tx.signatures.length).toBe(1); 25 | tx.sign(kp.keypair); 26 | expect(tx.signatures.length).toBe(2); 27 | }); 28 | it("should create keypair from random", () => { 29 | const rand = crypto.randomBytes(32); 30 | const kp = account.createKeypairFromRandom(rand); 31 | expect(kp.publicKey).toBeTruthy(); 32 | expect(kp.secretKey).toBeTruthy(); 33 | }); 34 | it("can init from string", () => { 35 | const kp = PublicKeypair.fromPublicKey( 36 | "GCPECGTX5RZWBJNH7Q3FNN4742R7OKMSP6G4ECCUX7Q5IGDCYYG2I447", 37 | ); 38 | expect(kp.publicKey).toBeTruthy(); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/test/accountService.test.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { Horizon } from "@stellar/stellar-sdk"; 3 | 4 | import { AccountService, SigningKeypair, Wallet } from "../src"; 5 | import { HORIZON_ORDER } from "../src/walletSdk/Types"; 6 | import { IssuedAssetId } from "../src/walletSdk/Asset"; 7 | 8 | let accountService: AccountService; 9 | let accountAddress: string; 10 | 11 | describe("Horizon", () => { 12 | // Creates testing stellar account with USDC trustline 13 | // in case it doesn't exist just yet 14 | beforeAll(async () => { 15 | const wallet = Wallet.TestNet(); 16 | const stellar = wallet.stellar(); 17 | accountService = stellar.account(); 18 | 19 | const fundingAccountKp = SigningKeypair.fromSecret( 20 | "SDJTZXPFPWRK4GHECLQBDFRDCNEZFA4PIZA475WQEGKTL4Y2QLS77DGD", 21 | ); 22 | 23 | // make sure funding account exists 24 | try { 25 | await stellar.server.loadAccount(fundingAccountKp.publicKey); 26 | } catch (e) { 27 | await axios.get( 28 | "https://friendbot.stellar.org/?addr=" + fundingAccountKp.publicKey, 29 | ); 30 | } 31 | 32 | // create test account 33 | const testingAccountKp = accountService.createKeypair(); 34 | await axios.get( 35 | "https://friendbot.stellar.org/?addr=" + testingAccountKp.publicKey, 36 | ); 37 | 38 | const txBuilder = await stellar.transaction({ 39 | sourceAddress: testingAccountKp, 40 | }); 41 | const asset = new IssuedAssetId("USDC", fundingAccountKp.publicKey); 42 | const addUsdcTx = txBuilder.addAssetSupport(asset).build(); 43 | addUsdcTx.sign(testingAccountKp.keypair); 44 | 45 | await stellar.submitTransaction(addUsdcTx); 46 | 47 | accountAddress = testingAccountKp.publicKey; 48 | }, 30000); 49 | 50 | it("should return stellar account details", async () => { 51 | const response = await accountService.getInfo({ accountAddress }); 52 | 53 | expect(response.account_id).toBe(accountAddress); 54 | expect(response.balances).toBeInstanceOf(Array); 55 | expect( 56 | response.balances.some( 57 | (balance) => 58 | (balance as Horizon.HorizonApi.BalanceLineAsset).asset_code === 59 | "USDC", 60 | ), 61 | ).toBeTruthy(); 62 | }); 63 | 64 | it("should error 404 in case account not found", async () => { 65 | // Any recently generated keypair won't be tied to a stellar account 66 | const publicKeyWithoutAccount = accountService.createKeypair().publicKey; 67 | 68 | try { 69 | await accountService.getInfo({ accountAddress: publicKeyWithoutAccount }); 70 | } catch (e) { 71 | expect(e?.response?.status).toBe(404); 72 | } 73 | }); 74 | 75 | it("should return stellar account operations", async () => { 76 | const response = await accountService.getHistory({ 77 | accountAddress, 78 | order: HORIZON_ORDER.ASC, 79 | }); 80 | 81 | expect(response.records).toBeInstanceOf(Array); 82 | expect(response.records[0]).toBeInstanceOf(Object); 83 | expect(response.records[0]).toHaveProperty("id"); 84 | expect(response.records[0]).toHaveProperty("type"); 85 | expect(response.records[0]).toHaveProperty("created_at"); 86 | expect( 87 | response.records.some( 88 | ({ type }) => 89 | type === Horizon.HorizonApi.OperationResponseType.createAccount, 90 | ), 91 | ).toBeTruthy(); 92 | expect( 93 | response.records.some( 94 | ({ type }) => 95 | type === Horizon.HorizonApi.OperationResponseType.changeTrust, 96 | ), 97 | ).toBeTruthy(); 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/test/customer.test.ts: -------------------------------------------------------------------------------- 1 | import { Sep12, SigningKeypair, Wallet } from "../src"; 2 | import axios from "axios"; 3 | import { getRandomString } from "./utils"; 4 | 5 | let wallet: Wallet; 6 | let accountKp: SigningKeypair; 7 | 8 | describe("Customer", () => { 9 | beforeAll(async () => { 10 | wallet = Wallet.TestNet(); 11 | const stellar = wallet.stellar(); 12 | const account = stellar.account(); 13 | accountKp = account.createKeypair(); 14 | await axios.get( 15 | "https://friendbot.stellar.org/?addr=" + accountKp.publicKey, 16 | ); 17 | }, 10000); 18 | 19 | test("Sep-12 methods work", async () => { 20 | const anchor = wallet.anchor({ homeDomain: "testanchor.stellar.org" }); 21 | 22 | const auth = await anchor.sep10(); 23 | const authToken = await auth.authenticate({ accountKp }); 24 | 25 | const sep12: Sep12 = await anchor.sep12(authToken); 26 | const customerType = "sep31-receiver"; 27 | const customerEmail = `${getRandomString(6)}@gmail.com`; 28 | 29 | // Add 30 | const addResp = await sep12.add({ 31 | sep9Info: { 32 | first_name: "john", 33 | last_name: "smith", 34 | email_address: customerEmail, 35 | type: customerType, 36 | }, 37 | transactionId: "abcd1234", 38 | }); 39 | expect(addResp.id).toBeTruthy(); 40 | const { id } = addResp; 41 | 42 | // Get 43 | const getResp = await sep12.getCustomer({ 44 | id, 45 | type: customerType, 46 | transactionId: "abcd1234", 47 | }); 48 | expect(Object.keys(getResp).sort()).toEqual( 49 | ["id", "provided_fields", "fields", "status"].sort(), 50 | ); 51 | expect(Object.keys(getResp?.provided_fields).sort()).toEqual( 52 | ["first_name", "last_name", "email_address"].sort(), 53 | ); 54 | expect(Object.keys(getResp?.fields).sort()).toEqual( 55 | [ 56 | "bank_account_number", 57 | "bank_number", 58 | "photo_id_front", 59 | "photo_id_back", 60 | ].sort(), 61 | ); 62 | 63 | // Update 64 | const updateResp = await sep12.update({ 65 | sep9Info: { 66 | first_name: "j", 67 | last_name: "s", 68 | email_address: "1" + customerEmail, 69 | bank_account_number: "12345", 70 | bank_number: "54321", 71 | }, 72 | sep9BinaryInfo: { 73 | photo_id_front: Buffer.from("test-front-image"), 74 | photo_id_back: Buffer.from("test-back-image"), 75 | }, 76 | id, 77 | transactionId: "abcd1234", 78 | }); 79 | expect(updateResp.id).toBeTruthy(); 80 | 81 | // Get again, check that the provided fields updated 82 | const getResp2 = await sep12.getCustomer({ 83 | id, 84 | type: customerType, 85 | transactionId: "abcd1234", 86 | }); 87 | expect(Object.keys(getResp2.fields).length).toBe(0); 88 | 89 | // Delete 90 | await sep12.delete(); 91 | }, 20000); 92 | }); 93 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/test/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | recovery-signer-migrate1: 4 | image: stellar/recoverysigner:latest 5 | depends_on: 6 | postgres1: 7 | condition: service_healthy 8 | restart: on-failure 9 | command: ["db", "migrate", "up"] 10 | environment: 11 | DB_URL: "postgresql://postgres:pg_password@postgres1:5432/pg_database1?sslmode=disable" 12 | 13 | recovery-signer-migrate2: 14 | image: stellar/recoverysigner:latest 15 | depends_on: 16 | postgres2: 17 | condition: service_healthy 18 | restart: on-failure 19 | command: ["db", "migrate", "up"] 20 | environment: 21 | DB_URL: "postgresql://postgres:pg_password@postgres2:5432/pg_database2?sslmode=disable" 22 | 23 | recovery-signer1: 24 | image: stellar/recoverysigner:latest 25 | ports: 26 | - "8000:8000" 27 | depends_on: 28 | - postgres1 29 | environment: 30 | DB_URL: "postgresql://postgres:pg_password@postgres1:5432/pg_database1?sslmode=disable" 31 | SIGNING_KEY: SAQFNCKPZ3ON5TSSEURAF4NPTZONPA37JPHQNHSLSRUNFP43MMT5LNH6 32 | FIREBASE_PROJECT_ID: "none" 33 | SEP10_JWKS: '{"keys":[{"kty":"EC","crv":"P-256","alg":"ES256","x":"dzqvhrMYwbmv7kcZK6L1oOATMFXG9wLFlnKfHf3E7FM","y":"Vb_wmcX-Zq2Hg2LFoXCEVWMwdJ01q41pSnxC3psunUY"}]}' 34 | PORT: 8000 35 | 36 | recovery-signer2: 37 | image: stellar/recoverysigner:latest 38 | ports: 39 | - "8002:8002" 40 | depends_on: 41 | - postgres2 42 | environment: 43 | DB_URL: "postgresql://postgres:pg_password@postgres2:5432/pg_database2?sslmode=disable" 44 | SIGNING_KEY: SA3Y2KQCPN6RAKLUISMY252QABWPQ3A65FBMZO2JJFKJ7O7VJNQ2PRYH # Use a different key for the second recovery signer 45 | FIREBASE_PROJECT_ID: "none" 46 | SEP10_JWKS: '{"keys":[{"kty":"EC","crv":"P-256","alg":"ES256","x":"dzqvhrMYwbmv7kcZK6L1oOATMFXG9wLFlnKfHf3E7FM","y":"Vb_wmcX-Zq2Hg2LFoXCEVWMwdJ01q41pSnxC3psunUY"}]}' 47 | PORT: 8002 48 | 49 | web-auth1: 50 | image: stellar/webauth:latest 51 | ports: 52 | - "8001:8001" 53 | environment: 54 | SIGNING_KEY: SDYHSG4V2JP5H66N2CXBFCOBTAUFWXGJVPKWY6OXSIPMYW743N62QX6U 55 | JWK: '{"kty":"EC","crv":"P-256","alg":"ES256","x":"dzqvhrMYwbmv7kcZK6L1oOATMFXG9wLFlnKfHf3E7FM","y":"Vb_wmcX-Zq2Hg2LFoXCEVWMwdJ01q41pSnxC3psunUY","d":"ivOMB4Wscz8ShvhwWDRyd-JJVfSMsjsz1oU3sNc-XJo"}' 56 | DOMAIN: test-domain 57 | AUTH_HOME_DOMAIN: test-domain 58 | JWT_ISSUER: test 59 | PORT: 8001 60 | 61 | web-auth2: 62 | image: stellar/webauth:latest 63 | ports: 64 | - "8003:8003" 65 | environment: 66 | SIGNING_KEY: SCAS7BUKVDL44A2BAP23RVAM6XXHB24YRCANQGDTP24HP7T6LPUFIGGU # Use a different key for the second web auth server 67 | JWK: '{"kty":"EC","crv":"P-256","alg":"ES256","x":"dzqvhrMYwbmv7kcZK6L1oOATMFXG9wLFlnKfHf3E7FM","y":"Vb_wmcX-Zq2Hg2LFoXCEVWMwdJ01q41pSnxC3psunUY","d":"ivOMB4Wscz8ShvhwWDRyd-JJVfSMsjsz1oU3sNc-XJo"}' 68 | DOMAIN: test-domain 69 | AUTH_HOME_DOMAIN: test-domain 70 | JWT_ISSUER: test 71 | PORT: 8003 72 | 73 | postgres1: 74 | image: postgres:14 75 | environment: 76 | POSTGRES_PASSWORD: pg_password 77 | POSTGRES_DB: pg_database1 78 | ports: 79 | - "5432:5432" 80 | healthcheck: 81 | test: ["CMD-SHELL", "pg_isready -U postgres"] 82 | interval: 10s 83 | timeout: 5s 84 | retries: 5 85 | 86 | postgres2: 87 | image: postgres:14 88 | environment: 89 | POSTGRES_PASSWORD: pg_password 90 | POSTGRES_DB: pg_database2 91 | ports: 92 | - "5433:5432" 93 | healthcheck: 94 | test: ["CMD-SHELL", "pg_isready -U postgres"] 95 | interval: 10s 96 | timeout: 5s 97 | retries: 5 98 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/test/e2e/README.md: -------------------------------------------------------------------------------- 1 | # How it works 2 | 3 | ## browser.test.ts 4 | 5 | This test uses playwright to load the browser bundle file into a browser 6 | environment and run its code. If there is a bug in how its built, 7 | window.WalletSDK will be undefined. 8 | 9 | ### To run 10 | 11 | $ yarn build $ yarn test browser.test.ts 12 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/test/e2e/browser.test.ts: -------------------------------------------------------------------------------- 1 | import { chromium, webkit } from "playwright"; 2 | 3 | describe("Test browser build", () => { 4 | const browsers = [ 5 | { name: "Chrome", instance: chromium }, 6 | { name: "Safari", instance: webkit }, 7 | ]; 8 | 9 | for (const b of browsers) { 10 | it( 11 | "works on " + b.name, 12 | async () => { 13 | await (async () => { 14 | const browser = await b.instance.launch(); 15 | const page = await browser.newPage(); 16 | 17 | await page.goto("https://stellar.org"); 18 | 19 | await page.addScriptTag({ 20 | path: "./lib/bundle_browser.js", 21 | }); 22 | 23 | // Use the Stellar SDK in the website's context 24 | const result = await page.evaluate(() => { 25 | let kp; 26 | try { 27 | const wal = (window as any).WalletSDK.Wallet.TestNet(); 28 | const account = wal.stellar().account(); 29 | 30 | kp = account.createKeypair(); 31 | } catch (e) { 32 | return { success: false }; 33 | } 34 | 35 | return { 36 | publicKey: kp.publicKey, 37 | secretKey: kp.secretKey, 38 | success: true, 39 | }; 40 | }); 41 | 42 | expect(result.publicKey).toBeTruthy(); 43 | expect(result.secretKey).toBeTruthy(); 44 | expect(result.success).toBeTruthy(); 45 | 46 | await browser.close(); 47 | })(); 48 | }, 49 | 15000, 50 | ); 51 | } 52 | }); 53 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/test/integration/README.md: -------------------------------------------------------------------------------- 1 | # Recovery Integration Tests 2 | 3 | ## How it works 4 | 5 | The recovery integration tests run different recovery scenarios against recovery 6 | signer and webauth servers. 2 recovery signer and 2 webauth servers are started 7 | in a docker-compose file (see test/docker/docker-compose.yml), to simulate a 8 | wallet interacting with 2 separate recovery servers. 9 | 10 | ## To run tests locally: 11 | 12 | ``` 13 | // start servers using docker 14 | $ docker-compose -f @stellar/typescript-wallet-sdk/test/docker/docker-compose.yml up 15 | 16 | // run tests 17 | $ yarn test:recovery:ci 18 | ``` 19 | 20 | # AnchorPlatform Integration Tests 21 | 22 | ## How it works 23 | 24 | This test works similar to the recovery integration tests. It spins up an 25 | anchorplatform image from the (java anchor sdk 26 | repo)[https://github.com/stellar/java-stellar-anchor-sdk] and runs tests 27 | against. 28 | 29 | ## To run tests locally: 30 | 31 | ``` 32 | yarn test:anchorplatform:ci 33 | ``` 34 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/test/integration/anchorplatform.test.ts: -------------------------------------------------------------------------------- 1 | import { Wallet } from "../../src"; 2 | import { IssuedAssetId } from "../../src/walletSdk/Asset"; 3 | import { DefaultAuthHeaderSigner } from "../../src/walletSdk/Auth/AuthHeaderSigner"; 4 | 5 | let wallet; 6 | let stellar; 7 | let anchor; 8 | let accountKp; 9 | const anchorUrl = "https://anchor-sep-server-dev.stellar.org/"; 10 | 11 | describe("Anchor Platform Integration Tests", () => { 12 | beforeAll(async () => { 13 | // Setup 14 | wallet = Wallet.TestNet(); 15 | stellar = wallet.stellar(); 16 | anchor = wallet.anchor({ homeDomain: anchorUrl, allowHttp: true }); 17 | accountKp = stellar.account().createKeypair(); 18 | await stellar.fundTestnetAccount(accountKp.publicKey); 19 | }, 30000); 20 | 21 | it("SEP-10 auth should work", async () => { 22 | const auth = await anchor.sep10(); 23 | const authToken = await auth.authenticate({ accountKp }); 24 | expect(authToken.token).toBeTruthy(); 25 | }, 30000); 26 | 27 | it("using DefaultAuthHeaderSigner should work", async () => { 28 | const auth = await anchor.sep10(); 29 | 30 | const authHeaderSigner = new DefaultAuthHeaderSigner(); 31 | const authToken = await auth.authenticate({ accountKp, authHeaderSigner }); 32 | expect(authToken.token).toBeTruthy(); 33 | }, 30000); 34 | 35 | it("SEP-12 KYC and SEP-6 should work", async () => { 36 | const auth = await anchor.sep10(); 37 | const authToken = await auth.authenticate({ accountKp }); 38 | 39 | // add USDC trustline 40 | const asset = new IssuedAssetId( 41 | "USDC", 42 | // anchor platform USDC issuer 43 | "GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP", 44 | ); 45 | const txBuilder = await stellar.transaction({ 46 | sourceAddress: accountKp, 47 | }); 48 | const addUsdcTx = txBuilder.addAssetSupport(asset).build(); 49 | addUsdcTx.sign(accountKp.keypair); 50 | await stellar.submitTransaction(addUsdcTx); 51 | 52 | // add SEP-12 KYC info 53 | const sep12 = await anchor.sep12(authToken); 54 | const sep12Resp = await sep12.add({ 55 | sep9Info: { 56 | first_name: "john", 57 | last_name: "smith", 58 | email_address: "123@gmail.com", 59 | bank_number: "12345", 60 | bank_account_number: "12345", 61 | }, 62 | }); 63 | expect(sep12Resp.id).toBeTruthy(); 64 | 65 | // SEP-6 deposit 66 | const sep6 = anchor.sep6(); 67 | const dResp = await sep6.deposit({ 68 | authToken, 69 | params: { 70 | asset_code: "USDC", 71 | account: accountKp.publicKey, 72 | type: "SEPA", 73 | }, 74 | }); 75 | expect(dResp.id).toBeTruthy(); 76 | 77 | // SEP-6 withdraw 78 | const wResp = await sep6.withdraw({ 79 | authToken, 80 | params: { 81 | asset_code: "USDC", 82 | account: accountKp.publicKey, 83 | type: "bank_account", 84 | dest: "123", 85 | dest_extra: "12345", 86 | }, 87 | }); 88 | expect(wResp.id).toBeTruthy(); 89 | }, 300000); 90 | 91 | it("SEP-24 should work", async () => { 92 | const assetCode = "USDC"; 93 | const auth = await anchor.sep10(); 94 | const authToken = await auth.authenticate({ accountKp }); 95 | 96 | const dResp = await anchor.sep24().deposit({ 97 | assetCode, 98 | authToken, 99 | }); 100 | const transactionId = dResp.id; 101 | expect(transactionId).toBeTruthy(); 102 | 103 | const wResp = await anchor.sep24().withdraw({ 104 | withdrawalAccount: accountKp.publicKey, 105 | assetCode, 106 | authToken, 107 | }); 108 | expect(wResp.id).toBeTruthy(); 109 | 110 | const transaction = await anchor.sep24().getTransactionBy({ 111 | authToken, 112 | id: transactionId, 113 | }); 114 | expect(transaction.id).toBeTruthy(); 115 | expect(transaction.status).toBe("incomplete"); 116 | 117 | const transactions = await anchor.sep24().getTransactionsForAsset({ 118 | authToken, 119 | assetCode, 120 | limit: 5, 121 | }); 122 | expect(transactions.length).toBe(2); 123 | }, 300000); 124 | 125 | it("SEP-38 should work", async () => { 126 | const auth = await anchor.sep10(); 127 | const authToken = await auth.authenticate({ accountKp }); 128 | const sep38 = anchor.sep38(authToken); 129 | 130 | // Price 131 | const resp = await sep38.price({ 132 | sellAsset: "iso4217:USD", 133 | buyAsset: 134 | "stellar:USDC:GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP", 135 | sellAmount: "5", 136 | context: "sep6", 137 | }); 138 | expect(resp.price).toBeTruthy(); 139 | 140 | // Create Quote 141 | const postResp = await sep38.requestQuote({ 142 | sell_asset: "iso4217:USD", 143 | buy_asset: 144 | "stellar:USDC:GDQOE23CFSUMSVQK4Y5JHPPYK73VYCNHZHA7ENKCV37P6SUEO6XQBKPP", 145 | sell_amount: "5", 146 | context: "sep6", 147 | }); 148 | expect(postResp.id).toBeTruthy(); 149 | 150 | // Get Quote 151 | const quoteId = postResp.id; 152 | const getResp = await sep38.getQuote(quoteId); 153 | expect(getResp.id).toBeTruthy(); 154 | }, 300000); 155 | }); 156 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/test/integration/recovery.test.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { Wallet } from "../../src"; 3 | 4 | import { 5 | RecoveryServer, 6 | RecoveryServerKey, 7 | RecoveryServerMap, 8 | RecoverableWalletConfig, 9 | RecoveryRole, 10 | RecoveryAccountIdentity, 11 | RecoveryType, 12 | } from "../../src/walletSdk/Types/recovery"; 13 | 14 | describe("Recovery Integration Tests", () => { 15 | it("should work", async () => { 16 | const wallet = Wallet.TestNet(); 17 | const stellar = wallet.stellar(); 18 | const accountService = stellar.account(); 19 | 20 | const server1Key: RecoveryServerKey = "server1"; 21 | const server1: RecoveryServer = { 22 | endpoint: "http://localhost:8000", 23 | authEndpoint: "http://localhost:8001", 24 | homeDomain: "test-domain", 25 | }; 26 | 27 | const server2Key: RecoveryServerKey = "server2"; 28 | const server2: RecoveryServer = { 29 | endpoint: "http://localhost:8002", 30 | authEndpoint: "http://localhost:8003", 31 | homeDomain: "test-domain", 32 | }; 33 | 34 | const servers: RecoveryServerMap = { 35 | [server1Key]: server1, 36 | [server2Key]: server2, 37 | }; 38 | 39 | const recovery = wallet.recovery({ servers }); 40 | 41 | // Create accounts 42 | 43 | const accountKp = accountService.createKeypair(); 44 | const deviceKp = accountService.createKeypair(); 45 | const recoveryKp = accountService.createKeypair(); 46 | 47 | try { 48 | await stellar.server.loadAccount(accountKp.publicKey); 49 | await stellar.server.loadAccount(deviceKp.publicKey); 50 | await stellar.server.loadAccount(recoveryKp.publicKey); 51 | } catch (e) { 52 | await axios.get( 53 | "https://friendbot.stellar.org/?addr=" + accountKp.publicKey, 54 | ); 55 | await axios.get( 56 | "https://friendbot.stellar.org/?addr=" + deviceKp.publicKey, 57 | ); 58 | await axios.get( 59 | "https://friendbot.stellar.org/?addr=" + recoveryKp.publicKey, 60 | ); 61 | } 62 | 63 | // Create SEP-30 identities 64 | 65 | const identity1: RecoveryAccountIdentity = { 66 | role: RecoveryRole.OWNER, 67 | authMethods: [ 68 | { 69 | type: RecoveryType.STELLAR_ADDRESS, 70 | value: recoveryKp.publicKey, 71 | }, 72 | ], 73 | }; 74 | 75 | const identity2: RecoveryAccountIdentity = { 76 | role: RecoveryRole.OWNER, 77 | authMethods: [ 78 | { 79 | type: RecoveryType.STELLAR_ADDRESS, 80 | value: recoveryKp.publicKey, 81 | }, 82 | { 83 | type: RecoveryType.EMAIL, 84 | value: "my-email@example.com", 85 | }, 86 | ], 87 | }; 88 | 89 | // Create recoverable wallet 90 | 91 | const config: RecoverableWalletConfig = { 92 | accountAddress: accountKp, 93 | deviceAddress: deviceKp, 94 | accountThreshold: { low: 10, medium: 10, high: 10 }, 95 | accountIdentity: { [server1Key]: [identity1], [server2Key]: [identity2] }, 96 | signerWeight: { device: 10, recoveryServer: 5 }, 97 | }; 98 | const recoverableWallet = await recovery.createRecoverableWallet(config); 99 | 100 | // Sign and submit 101 | 102 | recoverableWallet.transaction.sign(accountKp.keypair); 103 | await stellar.submitTransaction(recoverableWallet.transaction); 104 | 105 | let resp = await stellar.server.loadAccount(accountKp.publicKey); 106 | 107 | expect(resp.signers.map((obj) => obj.weight).sort((a, b) => a - b)).toEqual( 108 | [0, 5, 5, 10], 109 | ); 110 | expect( 111 | resp.signers.find((obj) => obj.key === accountKp.publicKey).weight, 112 | ).toBe(0); 113 | expect( 114 | resp.signers.find((obj) => obj.key === deviceKp.publicKey).weight, 115 | ).toBe(10); 116 | 117 | // Get Account Info 118 | 119 | const authToken1 = await recovery 120 | .sep10Auth(server1Key) 121 | .authenticate({ accountKp: recoveryKp }); 122 | 123 | const authMap = { [server1Key]: authToken1 }; 124 | 125 | const accountResp = await recovery.getAccountInfo(accountKp, authMap); 126 | expect(accountResp[server1Key].address).toBe(accountKp.publicKey); 127 | expect(accountResp[server1Key].identities[0].role).toBe("owner"); 128 | expect(accountResp[server1Key].signers.length).toBe(1); 129 | 130 | // Recover Wallet 131 | 132 | const authToken2 = await recovery 133 | .sep10Auth(server2Key) 134 | .authenticate({ accountKp: recoveryKp }); 135 | 136 | const recoverySignerAddress1 = recoverableWallet.signers[0]; 137 | const recoverySignerAddress2 = recoverableWallet.signers[1]; 138 | const newKp = accountService.createKeypair(); 139 | const signerMap = { 140 | [server1Key]: { 141 | signerAddress: recoverySignerAddress1, 142 | authToken: authToken1, 143 | }, 144 | [server2Key]: { 145 | signerAddress: recoverySignerAddress2, 146 | authToken: authToken2, 147 | }, 148 | }; 149 | const recoverTxn = await recovery.replaceDeviceKey( 150 | accountKp, 151 | newKp, 152 | signerMap, 153 | ); 154 | 155 | await stellar.submitTransaction(recoverTxn); 156 | 157 | resp = await stellar.server.loadAccount(accountKp.publicKey); 158 | 159 | expect( 160 | resp.signers.find((obj) => obj.key === deviceKp.publicKey), 161 | ).toBeFalsy(); 162 | expect(resp.signers.find((obj) => obj.key === newKp.publicKey).weight).toBe( 163 | 10, 164 | ); 165 | }, 60000); 166 | }); 167 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/test/recovery.test.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { xdr } from "@stellar/stellar-sdk"; 3 | 4 | import { SigningKeypair, Wallet } from "../src"; 5 | 6 | const testingAccountKp = SigningKeypair.fromSecret( 7 | "SDZZHNNOHOLFCAQ7XZZREXTXFTEPPT3L527WB2LVYCXODEDGTT6KBUSL", 8 | ); 9 | 10 | const accountSignerKp = SigningKeypair.fromSecret( 11 | "SDZF2OUDSU32XIQYVO53X2P4F7VYP72HJP7JR3RWTT3AFZSADENNL7YZ", 12 | ); 13 | 14 | const sponsorAccountKp = SigningKeypair.fromSecret( 15 | "SCIKQPLKAARVTUX76R3PPJ5PY5KANAJ4H5TXKBAZA4L2JIQCHVGVFFGS", 16 | ); 17 | 18 | let wallet: Wallet; 19 | 20 | describe("Recovery / Register Signers", () => { 21 | beforeAll(async () => { 22 | wallet = Wallet.TestNet(); 23 | const stellar = wallet.stellar(); 24 | 25 | // make sure testing accounts exist 26 | try { 27 | await stellar.server.loadAccount(testingAccountKp.publicKey); 28 | } catch (e) { 29 | await axios.get( 30 | "https://friendbot.stellar.org/?addr=" + testingAccountKp.publicKey, 31 | ); 32 | } 33 | 34 | try { 35 | await stellar.server.loadAccount(sponsorAccountKp.publicKey); 36 | } catch (e) { 37 | await axios.get( 38 | "https://friendbot.stellar.org/?addr=" + sponsorAccountKp.publicKey, 39 | ); 40 | } 41 | }, 60000); 42 | 43 | it("defaults work", async () => { 44 | const transaction = await wallet 45 | .recovery({ servers: {} }) 46 | .registerRecoveryServerSigners( 47 | testingAccountKp, 48 | [ 49 | { 50 | address: accountSignerKp, 51 | weight: 10, 52 | }, 53 | ], 54 | { 55 | low: 10, 56 | medium: 10, 57 | high: 10, 58 | }, 59 | ); 60 | 61 | expect(transaction.toXDR()).toBeTruthy(); 62 | expect(transaction.toEnvelope()).toBeInstanceOf(xdr.TransactionEnvelope); 63 | }); 64 | 65 | it("there are 3 operations in non-sponsored transaction", async () => { 66 | const transaction = await wallet 67 | .recovery({ servers: {} }) 68 | .registerRecoveryServerSigners( 69 | testingAccountKp, 70 | [ 71 | { 72 | address: accountSignerKp, 73 | weight: 10, 74 | }, 75 | ], 76 | { 77 | low: 10, 78 | medium: 10, 79 | high: 10, 80 | }, 81 | ); 82 | 83 | expect(transaction.operations.length).toBe(3); 84 | }); 85 | 86 | it("there are 5 operations in sponsored transaction", async () => { 87 | const transaction = await wallet 88 | .recovery({ servers: {} }) 89 | .registerRecoveryServerSigners( 90 | testingAccountKp, 91 | [ 92 | { 93 | address: accountSignerKp, 94 | weight: 10, 95 | }, 96 | ], 97 | { 98 | low: 10, 99 | medium: 10, 100 | high: 10, 101 | }, 102 | sponsorAccountKp, 103 | ); 104 | 105 | expect(transaction.operations.length).toBe(5); 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/test/sep38.test.ts: -------------------------------------------------------------------------------- 1 | import { Wallet } from "../src"; 2 | 3 | let wallet; 4 | let anchor; 5 | let sep38; 6 | let accountKp; 7 | 8 | describe("SEP-38", () => { 9 | beforeAll(() => { 10 | wallet = Wallet.TestNet(); 11 | anchor = wallet.anchor({ homeDomain: "testanchor.stellar.org" }); 12 | sep38 = anchor.sep38(); 13 | accountKp = accountKp = wallet.stellar().account().createKeypair(); 14 | }, 10000); 15 | 16 | it("should get Sep-38 anchor info", async () => { 17 | const resp = await sep38.info(); 18 | expect(resp.assets[0]).toBeTruthy(); 19 | 20 | const refreshed = await sep38.info(true); 21 | expect(refreshed.assets[0]).toBeTruthy(); 22 | }); 23 | 24 | it("should get Sep-38 prices", async () => { 25 | const auth = await anchor.sep10(); 26 | const authToken = await auth.authenticate({ accountKp }); 27 | sep38 = anchor.sep38(authToken); 28 | 29 | const resp = await sep38.prices({ 30 | sellAsset: "iso4217:USD", 31 | sellAmount: "5", 32 | sellDeliveryMethod: "ach_debit", 33 | }); 34 | expect(resp.buy_assets[0].asset).toBeTruthy(); 35 | }); 36 | 37 | it("should get Sep-38 price", async () => { 38 | const auth = await anchor.sep10(); 39 | const authToken = await auth.authenticate({ accountKp }); 40 | sep38 = anchor.sep38(authToken); 41 | 42 | const resp = await sep38.price({ 43 | sellAsset: "iso4217:USD", 44 | buyAsset: 45 | "stellar:SRT:GCDNJUBQSX7AJWLJACMJ7I4BC3Z47BQUTMHEICZLE6MU4KQBRYG5JY6B", 46 | sellAmount: "5", 47 | context: "sep6", 48 | }); 49 | expect(resp.price).toBeTruthy(); 50 | }); 51 | 52 | it("should request and get Sep-38 Quote", async () => { 53 | const auth = await anchor.sep10(); 54 | const authToken = await auth.authenticate({ accountKp }); 55 | sep38 = anchor.sep38(authToken); 56 | const postResp = await sep38.requestQuote({ 57 | sell_asset: "iso4217:USD", 58 | buy_asset: 59 | "stellar:SRT:GCDNJUBQSX7AJWLJACMJ7I4BC3Z47BQUTMHEICZLE6MU4KQBRYG5JY6B", 60 | sell_amount: "5", 61 | context: "sep6", 62 | sell_delivery_method: "ach_debit", 63 | }); 64 | expect(postResp.id).toBeTruthy(); 65 | 66 | const quoteId = postResp.id; 67 | const getResp = await sep38.getQuote(quoteId); 68 | 69 | expect(getResp.id).toBeTruthy(); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "baseUrl": "./", 5 | "outDir": "lib", 6 | "declaration": true, 7 | "declarationDir": "lib" 8 | }, 9 | "include": ["./"] 10 | } 11 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/test/utils/index.ts: -------------------------------------------------------------------------------- 1 | export const getRandomString = (length) => { 2 | const charset = 3 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 4 | let randomString = ""; 5 | 6 | for (let i = 0; i < length; i++) { 7 | const randomIndex = Math.floor(Math.random() * charset.length); 8 | randomString += charset[randomIndex]; 9 | } 10 | 11 | return randomString; 12 | }; 13 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "baseUrl": "src/", 5 | "outDir": "lib", 6 | "declaration": true, 7 | "declarationDir": "lib" 8 | }, 9 | "include": ["src"] 10 | } 11 | -------------------------------------------------------------------------------- /@stellar/typescript-wallet-sdk/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const webpack = require("webpack"); 3 | 4 | module.exports = (env = { NODE: false }) => { 5 | const isBrowser = !env.NODE; 6 | 7 | return { 8 | mode: "development", 9 | entry: "./src/index.ts", 10 | devtool: "source-map", 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.ts$/, 15 | use: "ts-loader", 16 | exclude: /node_modules/, 17 | }, 18 | ], 19 | }, 20 | resolve: { 21 | extensions: [".js", ".json", ".ts"], 22 | fallback: isBrowser 23 | ? { 24 | crypto: require.resolve("crypto-browserify"), 25 | http: require.resolve("stream-http"), 26 | https: require.resolve("https-browserify"), 27 | stream: require.resolve("stream-browserify"), 28 | url: require.resolve("url"), 29 | util: require.resolve("util"), 30 | vm: require.resolve("vm-browserify"), 31 | "process/browser": require.resolve("process/browser"), 32 | buffer: require.resolve("buffer"), 33 | } 34 | : {}, 35 | }, 36 | output: { 37 | library: "WalletSDK", 38 | libraryTarget: "umd", 39 | globalObject: "this", 40 | filename: `bundle${isBrowser ? "_browser" : ""}.js`, 41 | path: path.resolve(__dirname, "lib"), 42 | }, 43 | target: isBrowser ? "web" : "node", 44 | plugins: isBrowser 45 | ? [ 46 | new webpack.ProvidePlugin({ 47 | process: "process/browser", 48 | }), 49 | new webpack.ProvidePlugin({ 50 | Buffer: ["buffer", "Buffer"], 51 | }), 52 | ] 53 | : [], 54 | }; 55 | }; 56 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | Please read the 4 | [Contribution Guide](https://github.com/stellar/docs/blob/master/CONTRIBUTING.md). 5 | 6 | Then please 7 | [sign the Contributor License Agreement](https://docs.google.com/forms/d/1g7EF6PERciwn7zfmfke5Sir2n10yddGGSXyZsq98tVY/viewform?usp=send_form). 8 | 9 | # Releasing 10 | 11 | 1. Update package.json version file in all submodules to new version 12 | 13 | - all submodules should use same versioning 14 | 15 | 2. Update CHANGELOG.md in submodules that are updated 16 | 3. Commit changes 17 | 4. Trigger an npm publish using Github action for each updated submodule 18 | 5. Add a new release at: 19 | https://github.com/stellar/typescript-wallet-sdk/releases 20 | 21 | ## Npm Pipelines 22 | 23 | All npm pipelines can be found in .github/workflows 24 | 25 | 1. npmPublishSdk.yml 26 | 27 | - publishes typescript-wallet-sdk to npm 28 | 29 | 2. npmPublishSdkKM.yml 30 | 31 | - publishes typescript-wallet-sdk-km to npm 32 | 33 | 3. npmPublishSdkKM.yml 34 | 35 | - publishes typescript-wallet-sdk-soroban to npm 36 | 37 | 4. npmPublishBeta.yml 38 | 39 | - publishes a beta build of typescript-wallet-sdk on merges to the `develop` 40 | branch 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Typescript Wallet SDK Monorepo 2 | 3 | Typescript Wallet SDK is a library that allows developers to build wallet 4 | applications on the Stellar network faster. To jump right into how the main 5 | typescript-wallet-sdk module works you can 6 | [go here](./@stellar/typescript-wallet-sdk/README.md). 7 | 8 | ## Yarn Workspaces 9 | 10 | This repo uses a yarn worspace to create a monorepo that includes the main 11 | wallet sdk, and two modules for extending functionality: 12 | 13 | - [typescript-wallet-sdk](./@stellar/typescript-wallet-sdk/README.md) - the main 14 | sdk for building wallets on stellar 15 | - [typescript-wallet-sdk-km](./@stellar/typescript-wallet-sdk-km/README.md) - 16 | functionality for key managing 17 | - [typescript-wallet-sdk-soroban](./@stellar/typescript-wallet-sdk-soroban/README.md) - 18 | functionality for smart contracts on stellar 19 | 20 | ## Prerequisites 21 | 22 | You will need 23 | 24 | - Node (>=18): https://nodejs.org/en/download/ 25 | - Yarn (v1.22.5 or newer): https://classic.yarnpkg.com/en/docs/install 26 | 27 | ## Install and Build the Project 28 | 29 | ``` 30 | yarn install 31 | yarn build 32 | ``` 33 | 34 | - this will install and build for all sub modules 35 | 36 | ## Testing 37 | 38 | ``` 39 | yarn test 40 | ``` 41 | 42 | - this will run all jest unit tests for each submodule 43 | 44 | Some tests that are not ran as part of that suite (but run during ci/cd): 45 | 46 | - [integration tests](./@stellar/typescript-wallet-sdk/test/integration/README.md) 47 | - [e2e tests](./@stellar/typescript-wallet-sdk/test/e2e/README.md) 48 | 49 | ## Example code 50 | 51 | Example code can be found in the main sdk 52 | [examples directory](./@stellar/typescript-wallet-sdk/examples) 53 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { presets: ["@babel/preset-typescript", "@babel/preset-env"] }; 2 | -------------------------------------------------------------------------------- /docs/WalletGuide.md: -------------------------------------------------------------------------------- 1 | # Wallet SDK usage guide 2 | 3 | Please refer to 4 | [Stellar Docs](https://developers.stellar.org/docs/category/build-a-wallet) on 5 | how to use wallet sdk. 6 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const commonConfigs = { 2 | transformIgnorePatterns: [`/node_modules/(?!${["@stablelib"].join("|")})`], 3 | transform: { 4 | "^.+\\.(js|jsx|ts|tsx|mjs)$": ["babel-jest"], 5 | }, 6 | }; 7 | 8 | module.exports = { 9 | projects: [ 10 | { 11 | displayName: "Wallet SDK", 12 | roots: ["./@stellar/typescript-wallet-sdk"], 13 | testPathIgnorePatterns: ["/node_modules/", "/integration/", "/e2e/"], 14 | ...commonConfigs, 15 | }, 16 | { 17 | displayName: "Wallet SDK KM", 18 | roots: ["./@stellar/typescript-wallet-sdk-km"], 19 | testPathIgnorePatterns: ["/node_modules/"], 20 | ...commonConfigs, 21 | }, 22 | { 23 | displayName: "Wallet SDK Soroban", 24 | roots: ["./@stellar/typescript-wallet-sdk-soroban"], 25 | testPathIgnorePatterns: ["/node_modules/"], 26 | ...commonConfigs, 27 | }, 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "main": ".eslintrc.js", 4 | "workspaces": [ 5 | "@stellar/typescript-wallet-sdk", 6 | "@stellar/typescript-wallet-sdk-km", 7 | "@stellar/typescript-wallet-sdk-soroban" 8 | ], 9 | "scripts": { 10 | "prepare": "husky install", 11 | "lint": "eslint . --ext .ts", 12 | "test": "jest --watchAll", 13 | "test:ci": "jest --ci", 14 | "test:e2e:ci": "yarn workspace @stellar/typescript-wallet-sdk test:e2e:ci", 15 | "test:recovery:ci": "yarn workspace @stellar/typescript-wallet-sdk test:recovery:ci", 16 | "test:anchorplatform:ci": "yarn workspace @stellar/typescript-wallet-sdk test:anchorplatform:ci", 17 | "build": "yarn workspace @stellar/typescript-wallet-sdk build && yarn workspace @stellar/typescript-wallet-sdk-km build && yarn workspace @stellar/typescript-wallet-sdk-soroban build" 18 | }, 19 | "lint-staged": { 20 | "**/*.ts": [ 21 | "eslint --fix --max-warnings 0" 22 | ] 23 | }, 24 | "author": "", 25 | "license": "ISC" 26 | } 27 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require("@stellar/prettier-config"); 2 | --------------------------------------------------------------------------------