@veil-cash/sdk 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,231 @@
1
+ # Veil SDK Reference
2
+
3
+ Detailed payload spec and SDK function signatures for agent integration.
4
+
5
+ ## Unsigned payload spec
6
+
7
+ All `--unsigned` CLI output and programmatic builders target **Base mainnet** (chain ID 8453).
8
+
9
+ ### Payload shape
10
+
11
+ ```json
12
+ {
13
+ "to": "0x...",
14
+ "data": "0x...",
15
+ "value": "0",
16
+ "chainId": 8453
17
+ }
18
+ ```
19
+
20
+ ### Field reference
21
+
22
+ | Field | Type | Required | Description |
23
+ |-------|------|----------|-------------|
24
+ | `to` | `string` | yes | Target contract address (`0x` + 40 hex chars) |
25
+ | `data` | `string` | yes | ABI-encoded calldata (`0x` + hex) |
26
+ | `value` | `string` | yes | ETH amount in wei as a **string** (e.g. `"0"`, `"1000000000000000000"` for 1 ETH) |
27
+ | `chainId` | `number` | yes | `8453` (Base) |
28
+
29
+ `value` must always be a string. For register and USDC transactions it is `"0"`.
30
+ For ETH deposits it equals the deposit amount in wei.
31
+
32
+ ### CLI extra fields
33
+
34
+ The CLI may include an `action` or `step` field for context:
35
+
36
+ - **Register**: `"action": "register"` or `"action": "changeDepositKey"`
37
+ - **Deposit**: `"step": "approve"` (USDC only) and `"step": "deposit"`
38
+
39
+ These fields are informational and can be ignored by the signer.
40
+ For `veil register --unsigned --force`, the CLI checks chain state first and chooses `"register"` vs `"changeDepositKey"` based on whether the address is already registered.
41
+
42
+ ---
43
+
44
+ ## SDK build functions
45
+
46
+ All functions are exported from `@veil-cash/sdk`.
47
+
48
+ ### Keypair
49
+
50
+ ```typescript
51
+ import { Keypair, VEIL_SIGNED_MESSAGE } from '@veil-cash/sdk';
52
+ import type { MessageSigner } from '@veil-cash/sdk';
53
+
54
+ // Random keypair
55
+ const keypair = new Keypair();
56
+
57
+ // Restore from saved private key
58
+ const restored = new Keypair('0xSAVED_VEIL_KEY');
59
+
60
+ // Derive from Ethereum wallet key (same keypair as frontend login)
61
+ const derived = await Keypair.fromWalletKey('0xWALLET_KEY');
62
+
63
+ // Derive from pre-computed EIP-191 signature
64
+ const fromSig = Keypair.fromSignature('0xSIG');
65
+
66
+ // Derive via external signer callback
67
+ const fromSigner = await Keypair.fromSigner(async (msg) => {
68
+ return await externalSignerService.personalSign(msg);
69
+ });
70
+
71
+ // Public deposit key (register on-chain)
72
+ keypair.depositKey(); // 0x-prefixed, 130 hex chars
73
+
74
+ // Private key (store securely)
75
+ keypair.privkey; // 0x-prefixed, 66 hex chars
76
+ ```
77
+
78
+ ### Register
79
+
80
+ ```typescript
81
+ import { buildRegisterTx, buildChangeDepositKeyTx } from '@veil-cash/sdk';
82
+ import type { TransactionData } from '@veil-cash/sdk';
83
+
84
+ // First-time registration
85
+ const tx: TransactionData = buildRegisterTx(depositKey, '0xSIGNER_ADDRESS');
86
+ // tx = { to: '0x...', data: '0x...' }
87
+
88
+ // Update existing deposit key
89
+ const changeTx: TransactionData = buildChangeDepositKeyTx(newDepositKey, '0xSIGNER_ADDRESS');
90
+ ```
91
+
92
+ `TransactionData` type:
93
+
94
+ ```typescript
95
+ interface TransactionData {
96
+ to: `0x${string}`;
97
+ data: `0x${string}`;
98
+ value?: bigint;
99
+ }
100
+ ```
101
+
102
+ For register transactions `value` is `undefined` (use `"0"` when forwarding to signer).
103
+
104
+ ### Deposit
105
+
106
+ ```typescript
107
+ import {
108
+ buildDepositETHTx,
109
+ buildDepositUSDCTx,
110
+ buildApproveUSDCTx,
111
+ buildDepositTx,
112
+ } from '@veil-cash/sdk';
113
+
114
+ // ETH deposit
115
+ const ethTx = buildDepositETHTx({ depositKey, amount: '0.1' });
116
+ // ethTx.value = 100000000000000000n (bigint)
117
+
118
+ // USDC deposit (two-step)
119
+ const approveTx = buildApproveUSDCTx({ amount: '100' });
120
+ const usdcTx = buildDepositUSDCTx({ depositKey, amount: '100' });
121
+
122
+ // Generic (routes to ETH or USDC builder)
123
+ const tx = buildDepositTx({ depositKey, amount: '0.1', token: 'ETH' });
124
+ ```
125
+
126
+ When serializing for a signer: `value` must be converted to a string (`tx.value?.toString() ?? '0'`).
127
+
128
+ ### Balance
129
+
130
+ ```typescript
131
+ import { getQueueBalance, getPrivateBalance } from '@veil-cash/sdk';
132
+
133
+ // Queue balance (pending deposits)
134
+ const queue = await getQueueBalance({
135
+ address: '0x...',
136
+ pool: 'eth',
137
+ });
138
+
139
+ // Private balance (in-pool UTXOs)
140
+ const priv = await getPrivateBalance({
141
+ keypair,
142
+ pool: 'eth',
143
+ });
144
+ ```
145
+
146
+ ---
147
+
148
+ ## CLI quick reference
149
+
150
+ Install globally: `npm install -g @veil-cash/sdk`
151
+
152
+ ### Environment variables
153
+
154
+ | Variable | Description |
155
+ |----------|-------------|
156
+ | `VEIL_KEY` | Veil private key (for ZK proofs) |
157
+ | `DEPOSIT_KEY` | Veil deposit key (public) |
158
+ | `WALLET_KEY` | Ethereum wallet private key (for signing) |
159
+ | `SIGNER_ADDRESS` | Ethereum address for unsigned/query flows when signing is external |
160
+ | `RPC_URL` | Base RPC URL (optional, defaults to public RPC) |
161
+ | `RELAY_URL` | Override relay base URL for relayed CLI operations |
162
+
163
+ `WALLET_KEY` and `SIGNER_ADDRESS` are mutually exclusive. Use `SIGNER_ADDRESS` only for address-only CLI flows.
164
+
165
+ ### Commands
166
+
167
+ ```bash
168
+ veil init # Derive keypair from WALLET_KEY (saves to .env.veil)
169
+ veil init --generate # Generate random keypair
170
+ veil init --signature 0x... # Derive from pre-computed EIP-191 signature
171
+ veil init --force # Overwrite existing keypair without prompting
172
+ veil init --no-save # Print keypair without saving to disk
173
+ veil init --json # Output keypair as JSON (no prompts, no file save)
174
+
175
+ veil keypair # Show current keypair (from VEIL_KEY)
176
+ veil keypair --json # Show current keypair as JSON
177
+
178
+ veil status # Check config, signing mode, registration, and relay health
179
+ veil status --json # Machine-readable status
180
+
181
+ SIGNER_ADDRESS=0x... veil register --unsigned # Unsigned register payload
182
+ SIGNER_ADDRESS=0x... veil register --unsigned --force # Unsigned register/change-key payload (depends on chain state)
183
+ veil register --unsigned --address 0x... # Unsigned register payload (explicit address)
184
+ veil register --json # Register and output result as JSON
185
+
186
+ veil deposit ETH 0.1 --unsigned # Unsigned ETH deposit payload
187
+ veil deposit USDC 100 --unsigned # Unsigned USDC deposit payload(s)
188
+ veil deposit ETH 0.1 --json # Deposit and output result as JSON
189
+
190
+ veil balance # All pool balances
191
+ veil balance --pool eth # ETH pool only
192
+ veil balance --pool usdc # USDC pool only
193
+ veil balance --json # Machine-readable balances
194
+ veil balance queue --pool eth # Queue-only balance
195
+ veil balance queue --address 0x... --json # Queue balance for explicit address
196
+ veil balance private --pool eth # Private-only balance
197
+ veil balance private --json # Private balance as JSON
198
+ ```
199
+
200
+ ### Error format
201
+
202
+ All CLI errors output JSON with a standardized `errorCode`:
203
+
204
+ ```json
205
+ {
206
+ "success": false,
207
+ "errorCode": "VEIL_KEY_MISSING",
208
+ "error": "VEIL_KEY required. Set VEIL_KEY env"
209
+ }
210
+ ```
211
+
212
+ Common codes: `VEIL_KEY_MISSING`, `WALLET_KEY_MISSING`, `DEPOSIT_KEY_MISSING`,
213
+ `CONFIG_CONFLICT`, `INVALID_AMOUNT`, `INSUFFICIENT_BALANCE`, `CONTRACT_ERROR`, `RPC_ERROR`.
214
+
215
+ ---
216
+
217
+ ## Deposit minimums
218
+
219
+ | Asset | Minimum (net) | Notes |
220
+ |-------|--------------|-------|
221
+ | ETH | 0.01 | Fee (0.3%) added automatically via on-chain `getDepositAmountWithFee` |
222
+ | USDC | 10 | Fee (0.3%) added automatically via on-chain `getDepositAmountWithFee` |
223
+
224
+ The CLI amount is the **net** amount that lands in the pool. The fee is calculated on-chain and added to the transaction automatically — users do not need to account for it.
225
+
226
+ ---
227
+
228
+ ## Links
229
+
230
+ - npm: [@veil-cash/sdk](https://www.npmjs.com/package/@veil-cash/sdk)
231
+ - Veil Cash: [https://veil.cash](https://veil.cash)
package/src/abi.ts CHANGED
@@ -647,3 +647,175 @@ export const ERC20_ABI = [
647
647
  type: 'function',
648
648
  },
649
649
  ] as const;
650
+
651
+ /**
652
+ * Veil Forwarder Factory ABI
653
+ */
654
+ export const FORWARDER_FACTORY_ABI = [
655
+ {
656
+ inputs: [],
657
+ name: 'CONTRACT_VERSION',
658
+ outputs: [{ internalType: 'string', name: '', type: 'string' }],
659
+ stateMutability: 'view',
660
+ type: 'function',
661
+ },
662
+ {
663
+ inputs: [
664
+ { internalType: 'bytes32', name: '_salt', type: 'bytes32' },
665
+ { internalType: 'bytes', name: '_childDepositKey', type: 'bytes' },
666
+ { internalType: 'address', name: '_owner', type: 'address' },
667
+ ],
668
+ name: 'computeAddress',
669
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
670
+ stateMutability: 'view',
671
+ type: 'function',
672
+ },
673
+ {
674
+ inputs: [
675
+ { internalType: 'bytes32', name: '_salt', type: 'bytes32' },
676
+ { internalType: 'bytes', name: '_childDepositKey', type: 'bytes' },
677
+ { internalType: 'address', name: '_owner', type: 'address' },
678
+ ],
679
+ name: 'deploy',
680
+ outputs: [{ internalType: 'address', name: 'forwarder', type: 'address' }],
681
+ stateMutability: 'nonpayable',
682
+ type: 'function',
683
+ },
684
+ {
685
+ inputs: [],
686
+ name: 'relayer',
687
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
688
+ stateMutability: 'view',
689
+ type: 'function',
690
+ },
691
+ {
692
+ inputs: [],
693
+ name: 'veilEntry',
694
+ outputs: [{ internalType: 'address payable', name: '', type: 'address' }],
695
+ stateMutability: 'view',
696
+ type: 'function',
697
+ },
698
+ {
699
+ inputs: [],
700
+ name: 'usdc',
701
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
702
+ stateMutability: 'view',
703
+ type: 'function',
704
+ },
705
+ {
706
+ inputs: [],
707
+ name: 'owner',
708
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
709
+ stateMutability: 'view',
710
+ type: 'function',
711
+ },
712
+ ] as const;
713
+
714
+ /**
715
+ * Veil Forwarder ABI
716
+ */
717
+ export const FORWARDER_ABI = [
718
+ {
719
+ inputs: [],
720
+ name: 'CONTRACT_VERSION',
721
+ outputs: [{ internalType: 'string', name: '', type: 'string' }],
722
+ stateMutability: 'view',
723
+ type: 'function',
724
+ },
725
+ {
726
+ inputs: [
727
+ { internalType: 'address', name: '_token', type: 'address' },
728
+ { internalType: 'address', name: '_to', type: 'address' },
729
+ { internalType: 'uint256', name: '_amount', type: 'uint256' },
730
+ { internalType: 'uint256', name: '_nonce', type: 'uint256' },
731
+ { internalType: 'uint256', name: '_deadline', type: 'uint256' },
732
+ { internalType: 'bytes', name: '_signature', type: 'bytes' },
733
+ ],
734
+ name: 'withdraw',
735
+ outputs: [],
736
+ stateMutability: 'nonpayable',
737
+ type: 'function',
738
+ },
739
+ {
740
+ inputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
741
+ name: 'usedNonces',
742
+ outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
743
+ stateMutability: 'view',
744
+ type: 'function',
745
+ },
746
+ {
747
+ inputs: [],
748
+ name: 'owner',
749
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
750
+ stateMutability: 'view',
751
+ type: 'function',
752
+ },
753
+ {
754
+ inputs: [],
755
+ name: 'factory',
756
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
757
+ stateMutability: 'view',
758
+ type: 'function',
759
+ },
760
+ {
761
+ inputs: [],
762
+ name: 'childDepositKey',
763
+ outputs: [{ internalType: 'bytes', name: '', type: 'bytes' }],
764
+ stateMutability: 'view',
765
+ type: 'function',
766
+ },
767
+ {
768
+ inputs: [],
769
+ name: 'entry',
770
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
771
+ stateMutability: 'view',
772
+ type: 'function',
773
+ },
774
+ {
775
+ inputs: [],
776
+ name: 'usdc',
777
+ outputs: [{ internalType: 'address', name: '', type: 'address' }],
778
+ stateMutability: 'view',
779
+ type: 'function',
780
+ },
781
+ {
782
+ inputs: [],
783
+ name: 'sweepETH',
784
+ outputs: [],
785
+ stateMutability: 'nonpayable',
786
+ type: 'function',
787
+ },
788
+ {
789
+ inputs: [],
790
+ name: 'sweepUSDC',
791
+ outputs: [],
792
+ stateMutability: 'nonpayable',
793
+ type: 'function',
794
+ },
795
+ {
796
+ inputs: [],
797
+ name: 'eip712Domain',
798
+ outputs: [
799
+ { internalType: 'bytes1', name: 'fields', type: 'bytes1' },
800
+ { internalType: 'string', name: 'name', type: 'string' },
801
+ { internalType: 'string', name: 'version', type: 'string' },
802
+ { internalType: 'uint256', name: 'chainId', type: 'uint256' },
803
+ { internalType: 'address', name: 'verifyingContract', type: 'address' },
804
+ { internalType: 'bytes32', name: 'salt', type: 'bytes32' },
805
+ { internalType: 'uint256[]', name: 'extensions', type: 'uint256[]' },
806
+ ],
807
+ stateMutability: 'view',
808
+ type: 'function',
809
+ },
810
+ { type: 'error', name: 'ZeroAddress', inputs: [] },
811
+ { type: 'error', name: 'ZeroAmount', inputs: [] },
812
+ { type: 'error', name: 'InvalidDepositKey', inputs: [] },
813
+ { type: 'error', name: 'NotRelayer', inputs: [] },
814
+ { type: 'error', name: 'NoETHBalance', inputs: [] },
815
+ { type: 'error', name: 'NoTokenBalance', inputs: [] },
816
+ { type: 'error', name: 'TokenApproveFailed', inputs: [] },
817
+ { type: 'error', name: 'ETHTransferFailed', inputs: [] },
818
+ { type: 'error', name: 'NonceUsed', inputs: [] },
819
+ { type: 'error', name: 'Unauthorized', inputs: [] },
820
+ { type: 'error', name: 'DeadlineExpired', inputs: [] },
821
+ ] as const;
package/src/addresses.ts CHANGED
@@ -14,10 +14,16 @@ export const ADDRESSES: NetworkAddresses = {
14
14
  usdcPool: '0x5c50d58E49C59d112680c187De2Bf989d2a91242',
15
15
  usdcQueue: '0x5530241b24504bF05C9a22e95A1F5458888e6a9B',
16
16
  usdcToken: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
17
+ forwarderFactory: '0x2848Fd62293A1ff3b4a897E9FcD0e5962dcc8101',
17
18
  chainId: 8453,
18
19
  relayUrl: 'https://veil-relay.up.railway.app',
19
20
  } as const;
20
21
 
22
+ /**
23
+ * Veil forwarder EIP-712 contract version
24
+ */
25
+ export const FORWARDER_CONTRACT_VERSION = '1' as const;
26
+
21
27
  /**
22
28
  * Pool configuration (decimals, symbols, etc.)
23
29
  */
@@ -49,7 +55,9 @@ export function getAddresses(): NetworkAddresses {
49
55
  * @param pool - Pool identifier ('eth' or 'usdc')
50
56
  * @returns Pool contract address
51
57
  */
52
- export function getPoolAddress(pool: RelayPool): `0x${string}` {
58
+ export function getPoolAddress(
59
+ pool: RelayPool
60
+ ): `0x${string}` {
53
61
  const addresses = getAddresses();
54
62
  switch (pool) {
55
63
  case 'eth': return addresses.ethPool;
@@ -63,7 +71,9 @@ export function getPoolAddress(pool: RelayPool): `0x${string}` {
63
71
  * @param pool - Pool identifier ('eth' or 'usdc')
64
72
  * @returns Queue contract address
65
73
  */
66
- export function getQueueAddress(pool: RelayPool): `0x${string}` {
74
+ export function getQueueAddress(
75
+ pool: RelayPool
76
+ ): `0x${string}` {
67
77
  const addresses = getAddresses();
68
78
  switch (pool) {
69
79
  case 'eth': return addresses.ethQueue;
@@ -72,6 +82,14 @@ export function getQueueAddress(pool: RelayPool): `0x${string}` {
72
82
  }
73
83
  }
74
84
 
85
+ /**
86
+ * Get the forwarder factory contract address
87
+ * @returns Forwarder factory address for Base mainnet
88
+ */
89
+ export function getForwarderFactoryAddress(): `0x${string}` {
90
+ return getAddresses().forwarderFactory;
91
+ }
92
+
75
93
  /**
76
94
  * Get Relay URL
77
95
  * @returns Relay URL for Base mainnet
package/src/balance.ts CHANGED
@@ -101,14 +101,14 @@ export async function getQueueBalance(options: {
101
101
  depositKey: `0x${string}`;
102
102
  };
103
103
 
104
- // Check if this deposit belongs to the user
104
+ // Report the net shielded amount so queue totals match what will land in private balance.
105
105
  if (deposit.fallbackReceiver.toLowerCase() === address.toLowerCase()) {
106
- totalQueueBalance += deposit.amountIn;
106
+ totalQueueBalance += deposit.shieldAmount;
107
107
  pendingDeposits.push({
108
108
  nonce: nonce.toString(),
109
109
  status: DEPOSIT_STATUS_MAP[deposit.status as keyof typeof DEPOSIT_STATUS_MAP] || 'pending',
110
- amount: formatUnits(deposit.amountIn, poolConfig.decimals),
111
- amountWei: deposit.amountIn.toString(),
110
+ amount: formatUnits(deposit.shieldAmount, poolConfig.decimals),
111
+ amountWei: deposit.shieldAmount.toString(),
112
112
  timestamp: new Date(Number(deposit.timestamp) * 1000).toISOString(),
113
113
  });
114
114
  }