Build a TypeScript script that deposits assets into a DeFindex vault and transfers the minted
dfTokens to another wallet on the Stellar testnet.
## Technical Context
- DeFindex vaults are Soroban smart contracts on Stellar that accept asset deposits and mint
dfTokens (vault share tokens) in return
- The vault contract itself implements the Soroban token interface, so dfTokens can be
transferred by calling `transfer` directly on the vault contract address
- The deposit function returns a tuple where index [1] is the number of dfTokens minted (i128)
## Required Dependencies
```bash
npm install @defindex/sdk @stellar/stellar-sdk
npm install -D typescript tsx @types/node dotenv
```
Use your preferred package manager (`npm`, `yarn`, etc.). Install the latest versions.
## Environment Variables (.env)
```
DEFINDEX_API_KEY= # DeFindex API key (sk_...)
STELLAR_SECRET_KEY= # Wallet A secret key (depositor/signer)
RECEIVER_ADDRESS= # Wallet B public key (receives dfTokens)
SOROBAN_RPC=your testnet soroban rpc url
```
## Implementation Requirements
Create `src/index.ts` with the following structure:
### 1. Constants
- `VAULT_ADDRESS`: The target vault contract address (user must configure)
- `DECIMALS = 7` (Stellar standard)
- `DEPOSIT_AMOUNT`: Human-readable amount, converted to raw (amount * 10^7)
### 2. `loadEnv()` function
- Load and validate all four environment variables
- Exit with error if any are missing
- Return typed config object
### 3. `sendAndConfirm(rpcServer, transaction)` helper
- Send transaction to Soroban RPC via `rpcServer.sendTransaction()`
- Check status is 'PENDING', otherwise parse error from `response.errorResult`
- Poll `rpcServer.getTransaction(hash)` every 2 seconds until SUCCESS or FAILED (max 30 polls / 60s timeout)
- On SUCCESS, return `{ txHash, returnValue }` from `GetSuccessfulTransactionResponse`
### 4. `deposit(sdk, rpcServer, keypair)` function
- Use `sdk.depositToVault(VAULT_ADDRESS, { amounts: [RAW_AMOUNT], caller, invest: true },
SupportedNetworks.TESTNET)` to build unsigned XDR
- Parse XDR with `TransactionBuilder.fromXDR(xdr, Networks.TESTNET)`
- Sign with keypair, send via `sendAndConfirm()`
- Parse return value: `StellarSdk.scValToNative(returnValue)` returns an array,
index [1] is dfTokens minted as bigint
- Return `{ txHash, dfTokensMinted }`
### 5. `transferDfTokens(rpcServer, keypair, toAddress, amount)` function
- Create `new Contract(VAULT_ADDRESS)` instance
- Build operation: `contract.call('transfer', Address(from).toScVal(),
Address(to).toScVal(), nativeToScVal(amount, { type: 'i128' }))`
- Get account via `rpcServer.getAccount(from)`
- Build transaction with `TransactionBuilder`, fee `BASE_FEE`,
network `Networks.TESTNET`, timeout 30s
- Simulate with `rpcServer.simulateTransaction(tx)`, check for errors
with `rpc.Api.isSimulationError()`
- Assemble with `rpc.assembleTransaction(tx, simulation).build()`
- Sign and send via `sendAndConfirm()`
### 6. `main()` function
- Load env, create Keypair, rpc.Server, and DefindexSDK instances
- Step 1: Call deposit, log the deposit tx hash and dfTokens minted
- Step 2: Call transferDfTokens with the minted amount, log the transfer tx hash
- Print summary with all transaction details
## Run Script
Add `"start": "tsx src/index.ts"` to package.json scripts.
## Important Notes
- Use `Networks.TESTNET` and `SupportedNetworks.TESTNET` for testnet
- For production, switch to `Networks.PUBLIC` / `SupportedNetworks.MAINNET`
and a mainnet RPC endpoint
- All amounts use 7 decimals (stroops)
- The vault contract address is used both for deposit (via SDK) and for the
transfer call (as a Contract instance)
- Handle errors with try/catch — re-throw or call `process.exit(1)` on fatal errors
- Validate the deposit return shape before accessing positional indices