@l.x/config 1.0.3 → 1.0.5
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.
- package/.depcheckrc +9 -0
- package/.eslintrc.js +20 -0
- package/LICENSE +122 -0
- package/README.md +31 -0
- package/__tests__/brand-and-legal.test.ts +385 -0
- package/env.d.ts +64 -0
- package/env.native.d.ts +39 -0
- package/package.json +36 -1
- package/project.json +18 -0
- package/src/brand.ts +231 -0
- package/src/config-types.ts +48 -0
- package/src/getConfig.native.ts +124 -0
- package/src/getConfig.ts +6 -0
- package/src/getConfig.web.ts +69 -0
- package/src/index.ts +4 -0
- package/src/legal.ts +55 -0
- package/tsconfig.json +32 -0
- package/tsconfig.lint.json +8 -0
- package/vitest.config.ts +26 -0
- package/index.d.ts +0 -1
- package/index.js +0 -1
package/.depcheckrc
ADDED
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
extends: ['@luxamm/eslint-config/lib'],
|
|
3
|
+
parserOptions: {
|
|
4
|
+
tsconfigRootDir: __dirname,
|
|
5
|
+
},
|
|
6
|
+
overrides: [
|
|
7
|
+
{
|
|
8
|
+
files: ['*.ts', '*.tsx'],
|
|
9
|
+
rules: {
|
|
10
|
+
'no-relative-import-paths/no-relative-import-paths': [
|
|
11
|
+
'error',
|
|
12
|
+
{
|
|
13
|
+
allowSameFolder: false,
|
|
14
|
+
prefix: '@luxexchange/config',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Lux Ecosystem License
|
|
2
|
+
Version 1.2, December 2025
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2020-2025 Lux Industries Inc.
|
|
5
|
+
All rights reserved.
|
|
6
|
+
|
|
7
|
+
TECHNOLOGY PORTFOLIO - PATENT APPLICATIONS PLANNED
|
|
8
|
+
Contact: licensing@lux.network
|
|
9
|
+
|
|
10
|
+
================================================================================
|
|
11
|
+
TERMS AND CONDITIONS
|
|
12
|
+
================================================================================
|
|
13
|
+
|
|
14
|
+
1. DEFINITIONS
|
|
15
|
+
|
|
16
|
+
"Lux Primary Network" means the official Lux blockchain with Network ID=1
|
|
17
|
+
and EVM Chain ID=96369.
|
|
18
|
+
|
|
19
|
+
"Authorized Network" means the Lux Primary Network, official testnets/devnets,
|
|
20
|
+
and any L1/L2/L3 chain descending from the Lux Primary Network.
|
|
21
|
+
|
|
22
|
+
"Descending Chain" means an L1/L2/L3 chain built on, anchored to, or deriving
|
|
23
|
+
security from the Lux Primary Network or its authorized testnets.
|
|
24
|
+
|
|
25
|
+
"Research Use" means non-commercial academic research, education, personal
|
|
26
|
+
study, or evaluation purposes.
|
|
27
|
+
|
|
28
|
+
"Commercial Use" means any use in connection with a product or service
|
|
29
|
+
offered for sale or fee, internal use by a for-profit entity, or any use
|
|
30
|
+
to generate revenue.
|
|
31
|
+
|
|
32
|
+
2. GRANT OF LICENSE
|
|
33
|
+
|
|
34
|
+
Subject to these terms, Lux Industries Inc grants you a non-exclusive,
|
|
35
|
+
royalty-free license to:
|
|
36
|
+
|
|
37
|
+
(a) Use for Research Use without restriction;
|
|
38
|
+
|
|
39
|
+
(b) Operate on the Lux Primary Network (Network ID=1, EVM Chain ID=96369);
|
|
40
|
+
|
|
41
|
+
(c) Operate on official Lux testnets and devnets;
|
|
42
|
+
|
|
43
|
+
(d) Operate L1/L2/L3 chains descending from the Lux Primary Network;
|
|
44
|
+
|
|
45
|
+
(e) Build applications within the Lux ecosystem;
|
|
46
|
+
|
|
47
|
+
(f) Contribute improvements back to the original repositories.
|
|
48
|
+
|
|
49
|
+
3. RESTRICTIONS
|
|
50
|
+
|
|
51
|
+
Without a commercial license from Lux Industries Inc, you may NOT:
|
|
52
|
+
|
|
53
|
+
(a) Fork the Lux Network or any Lux software;
|
|
54
|
+
|
|
55
|
+
(b) Create competing networks not descending from Lux Primary Network;
|
|
56
|
+
|
|
57
|
+
(c) Use for Commercial Use outside the Lux ecosystem;
|
|
58
|
+
|
|
59
|
+
(d) Sublicense or transfer rights outside the Lux ecosystem;
|
|
60
|
+
|
|
61
|
+
(e) Use to create competing blockchain networks, exchanges, custody
|
|
62
|
+
services, or cryptographic systems outside the Lux ecosystem.
|
|
63
|
+
|
|
64
|
+
4. NO FORKS POLICY
|
|
65
|
+
|
|
66
|
+
Lux Industries Inc maintains ZERO TOLERANCE for unauthorized forks.
|
|
67
|
+
Any fork or deployment on an unauthorized network constitutes:
|
|
68
|
+
|
|
69
|
+
(a) Breach of this license;
|
|
70
|
+
(b) Grounds for immediate legal action.
|
|
71
|
+
|
|
72
|
+
5. RIGHTS RESERVATION
|
|
73
|
+
|
|
74
|
+
All rights not explicitly granted are reserved by Lux Industries Inc.
|
|
75
|
+
|
|
76
|
+
We plan to apply for patent protection for the technology in this
|
|
77
|
+
repository. Any implementation outside the Lux ecosystem may require
|
|
78
|
+
a separate commercial license.
|
|
79
|
+
|
|
80
|
+
6. DISCLAIMER OF WARRANTY
|
|
81
|
+
|
|
82
|
+
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
83
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
84
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
85
|
+
|
|
86
|
+
7. LIMITATION OF LIABILITY
|
|
87
|
+
|
|
88
|
+
IN NO EVENT SHALL LUX INDUSTRIES INC BE LIABLE FOR ANY CLAIM, DAMAGES
|
|
89
|
+
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
90
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE.
|
|
91
|
+
|
|
92
|
+
8. TERMINATION
|
|
93
|
+
|
|
94
|
+
This license terminates immediately upon any breach, including but not
|
|
95
|
+
limited to deployment on unauthorized networks or creation of forks.
|
|
96
|
+
|
|
97
|
+
9. GOVERNING LAW
|
|
98
|
+
|
|
99
|
+
This License shall be governed by the laws of the State of Delaware.
|
|
100
|
+
|
|
101
|
+
10. COMMERCIAL LICENSING
|
|
102
|
+
|
|
103
|
+
For commercial use outside the Lux ecosystem:
|
|
104
|
+
|
|
105
|
+
Lux Industries Inc.
|
|
106
|
+
Email: licensing@lux.network
|
|
107
|
+
Subject: Commercial License Request
|
|
108
|
+
|
|
109
|
+
================================================================================
|
|
110
|
+
TL;DR
|
|
111
|
+
================================================================================
|
|
112
|
+
|
|
113
|
+
- Research/academic use = OK
|
|
114
|
+
- Lux Primary Network (Network ID=1, Chain ID=96369) = OK
|
|
115
|
+
- L1/L2/L3 chains descending from Lux Primary Network = OK
|
|
116
|
+
- Commercial products outside Lux ecosystem = Contact licensing@lux.network
|
|
117
|
+
- Forks = Absolutely not
|
|
118
|
+
|
|
119
|
+
================================================================================
|
|
120
|
+
|
|
121
|
+
See LP-0012 for full licensing documentation:
|
|
122
|
+
https://github.com/luxfi/lps/blob/main/LPs/lp-0012-ecosystem-licensing.md
|
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @universe/config
|
|
2
|
+
|
|
3
|
+
Configuration management package for the Uniswap Universe monorepo.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package provides centralized configuration management for all Uniswap applications (web, mobile, and extension). It handles environment variables and provides a platform-specific implementation for accessing configuration values.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { getConfig } from '@universe/config'
|
|
13
|
+
|
|
14
|
+
const config = getConfig()
|
|
15
|
+
console.log(config.infuraKey)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Platform Support
|
|
19
|
+
|
|
20
|
+
- **Web/Extension**: Uses `process.env` directly
|
|
21
|
+
- **Mobile**: Uses `react-native-dotenv` for environment variable management
|
|
22
|
+
|
|
23
|
+
## Configuration Values
|
|
24
|
+
|
|
25
|
+
See `src/config-types.ts` for the complete list of configuration options.
|
|
26
|
+
|
|
27
|
+
## Environment Variable Naming
|
|
28
|
+
|
|
29
|
+
- **Web**: Variables must be prefixed with `REACT_APP_`
|
|
30
|
+
- **Extension**: Variables use standard naming without prefix
|
|
31
|
+
- **Mobile**: Variables use standard naming without prefix, but also require `react-native-dotenv` setup
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
2
|
+
import { brand, loadBrandConfig } from '../src/brand'
|
|
3
|
+
import type { BrandConfig } from '../src/brand'
|
|
4
|
+
import {
|
|
5
|
+
LEGAL_UPDATED,
|
|
6
|
+
LEGAL_URLS,
|
|
7
|
+
FOOTER_DISCLAIMER,
|
|
8
|
+
REGULATORY_NOTICE,
|
|
9
|
+
COOKIE_NOTICE,
|
|
10
|
+
NON_CUSTODIAL_NOTICE,
|
|
11
|
+
getLegalUrls,
|
|
12
|
+
} from '../src/legal'
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// 1. BrandConfig interface completeness
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
describe('BrandConfig interface completeness', () => {
|
|
18
|
+
const requiredFields: (keyof BrandConfig)[] = [
|
|
19
|
+
'name',
|
|
20
|
+
'title',
|
|
21
|
+
'description',
|
|
22
|
+
'legalEntity',
|
|
23
|
+
'walletName',
|
|
24
|
+
'protocolName',
|
|
25
|
+
'copyrightHolder',
|
|
26
|
+
'appDomain',
|
|
27
|
+
'docsDomain',
|
|
28
|
+
'infoDomain',
|
|
29
|
+
'gatewayDomain',
|
|
30
|
+
'wsDomain',
|
|
31
|
+
'helpUrl',
|
|
32
|
+
'termsUrl',
|
|
33
|
+
'privacyUrl',
|
|
34
|
+
'downloadUrl',
|
|
35
|
+
'complianceEmail',
|
|
36
|
+
'supportEmail',
|
|
37
|
+
'twitter',
|
|
38
|
+
'github',
|
|
39
|
+
'discord',
|
|
40
|
+
'logoUrl',
|
|
41
|
+
'faviconUrl',
|
|
42
|
+
'primaryColor',
|
|
43
|
+
'defaultChainId',
|
|
44
|
+
'supportedChainIds',
|
|
45
|
+
'walletConnectProjectId',
|
|
46
|
+
'insightsHost',
|
|
47
|
+
'insightsApiKey',
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
it.each(requiredFields)('brand object has field "%s"', (field) => {
|
|
51
|
+
expect(brand).toHaveProperty(field)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('has exactly the expected number of fields', () => {
|
|
55
|
+
expect(Object.keys(brand).length).toBe(requiredFields.length)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// 2. Default brand values are defined (not undefined)
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
describe('default brand values are defined', () => {
|
|
63
|
+
it('faviconUrl defaults to /favicon.ico', () => {
|
|
64
|
+
expect(brand.faviconUrl).toBe('/favicon.ico')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('primaryColor defaults to #FC72FF', () => {
|
|
68
|
+
expect(brand.primaryColor).toBe('#FC72FF')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('defaultChainId defaults to 1', () => {
|
|
72
|
+
expect(brand.defaultChainId).toBe(1)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('supportedChainIds is an array', () => {
|
|
76
|
+
expect(Array.isArray(brand.supportedChainIds)).toBe(true)
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// 3. legalEntity field exists
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
describe('legalEntity field', () => {
|
|
84
|
+
it('exists on the brand object', () => {
|
|
85
|
+
expect(brand).toHaveProperty('legalEntity')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('is a string', () => {
|
|
89
|
+
expect(typeof brand.legalEntity).toBe('string')
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// 4. LEGAL_UPDATED is valid ISO date (YYYY-MM-DD)
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
describe('LEGAL_UPDATED', () => {
|
|
97
|
+
it('matches YYYY-MM-DD format', () => {
|
|
98
|
+
expect(LEGAL_UPDATED).toMatch(/^\d{4}-\d{2}-\d{2}$/)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('parses to a valid date', () => {
|
|
102
|
+
const date = new Date(LEGAL_UPDATED)
|
|
103
|
+
expect(date.toString()).not.toBe('Invalid Date')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('is not in the future beyond a reasonable window', () => {
|
|
107
|
+
const date = new Date(LEGAL_UPDATED)
|
|
108
|
+
const oneYearFromNow = new Date()
|
|
109
|
+
oneYearFromNow.setFullYear(oneYearFromNow.getFullYear() + 1)
|
|
110
|
+
expect(date.getTime()).toBeLessThanOrEqual(oneYearFromNow.getTime())
|
|
111
|
+
})
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// 5. LEGAL_URLS paths are relative or absolute
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
describe('LEGAL_URLS', () => {
|
|
118
|
+
it('terms path starts with /', () => {
|
|
119
|
+
expect(LEGAL_URLS.terms).toMatch(/^\//)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('privacy path starts with /', () => {
|
|
123
|
+
expect(LEGAL_URLS.privacy).toMatch(/^\//)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('regulatory is a full URL', () => {
|
|
127
|
+
expect(LEGAL_URLS.regulatory).toMatch(/^https?:\/\//)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('email links use mailto:', () => {
|
|
131
|
+
expect(LEGAL_URLS.compliance).toMatch(/^mailto:/)
|
|
132
|
+
expect(LEGAL_URLS.legal).toMatch(/^mailto:/)
|
|
133
|
+
expect(LEGAL_URLS.privacyEmail).toMatch(/^mailto:/)
|
|
134
|
+
expect(LEGAL_URLS.security).toMatch(/^mailto:/)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('LP document links are full URLs', () => {
|
|
138
|
+
expect(LEGAL_URLS.lp3103).toMatch(/^https?:\/\//)
|
|
139
|
+
expect(LEGAL_URLS.lp3104).toMatch(/^https?:\/\//)
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// 6. FOOTER_DISCLAIMER contains key phrases
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
describe('FOOTER_DISCLAIMER', () => {
|
|
147
|
+
it('mentions "experimental"', () => {
|
|
148
|
+
expect(FOOTER_DISCLAIMER.toLowerCase()).toContain('experimental')
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('mentions "not legal"', () => {
|
|
152
|
+
expect(FOOTER_DISCLAIMER.toLowerCase()).toContain('not legal')
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('mentions "non-custodial"', () => {
|
|
156
|
+
expect(FOOTER_DISCLAIMER.toLowerCase()).toContain('non-custodial')
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('mentions "at your own risk"', () => {
|
|
160
|
+
expect(FOOTER_DISCLAIMER.toLowerCase()).toContain('at your own risk')
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// 7. NON_CUSTODIAL_NOTICE contains key phrases
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
describe('NON_CUSTODIAL_NOTICE', () => {
|
|
168
|
+
it('mentions "never have access"', () => {
|
|
169
|
+
expect(NON_CUSTODIAL_NOTICE.toLowerCase()).toContain('never have access')
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('mentions "private keys"', () => {
|
|
173
|
+
expect(NON_CUSTODIAL_NOTICE.toLowerCase()).toContain('private keys')
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it('mentions "irreversible"', () => {
|
|
177
|
+
expect(NON_CUSTODIAL_NOTICE.toLowerCase()).toContain('irreversible')
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
// 8. REGULATORY_NOTICE does NOT contain hardcoded brand name "Lux"
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
describe('REGULATORY_NOTICE', () => {
|
|
185
|
+
it('does not contain the hardcoded brand name "Lux"', () => {
|
|
186
|
+
expect(REGULATORY_NOTICE).not.toMatch(/\bLux\b/)
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('is brand-agnostic (no proper brand nouns)', () => {
|
|
190
|
+
expect(REGULATORY_NOTICE).not.toMatch(/\bZoo\b/)
|
|
191
|
+
expect(REGULATORY_NOTICE).not.toMatch(/\bPars\b/)
|
|
192
|
+
expect(REGULATORY_NOTICE).not.toMatch(/\bHanzo\b/)
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
// 9. COOKIE_NOTICE mentions no tracking
|
|
198
|
+
// ---------------------------------------------------------------------------
|
|
199
|
+
describe('COOKIE_NOTICE', () => {
|
|
200
|
+
it('mentions "no tracking cookies"', () => {
|
|
201
|
+
expect(COOKIE_NOTICE.toLowerCase()).toContain('no tracking cookies')
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('mentions "no behavioral profiling"', () => {
|
|
205
|
+
expect(COOKIE_NOTICE.toLowerCase()).toContain('no behavioral profiling')
|
|
206
|
+
})
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
// 10. getLegalUrls() generates correct URLs
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
describe('getLegalUrls()', () => {
|
|
213
|
+
it('returns terms URL with the given domain', () => {
|
|
214
|
+
const urls = getLegalUrls('lux.exchange')
|
|
215
|
+
expect(urls.terms).toBe('https://lux.exchange/terms')
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('returns privacy URL with the given domain', () => {
|
|
219
|
+
const urls = getLegalUrls('lux.exchange')
|
|
220
|
+
expect(urls.privacy).toBe('https://lux.exchange/privacy')
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
it('returns regulatory URL from LEGAL_URLS constant', () => {
|
|
224
|
+
const urls = getLegalUrls('lux.exchange')
|
|
225
|
+
expect(urls.regulatory).toBe(LEGAL_URLS.regulatory)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('works with arbitrary domains', () => {
|
|
229
|
+
const urls = getLegalUrls('zoo.exchange')
|
|
230
|
+
expect(urls.terms).toBe('https://zoo.exchange/terms')
|
|
231
|
+
expect(urls.privacy).toBe('https://zoo.exchange/privacy')
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
it('handles subdomain domains', () => {
|
|
235
|
+
const urls = getLegalUrls('app.pars.market')
|
|
236
|
+
expect(urls.terms).toBe('https://app.pars.market/terms')
|
|
237
|
+
expect(urls.privacy).toBe('https://app.pars.market/privacy')
|
|
238
|
+
})
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
// 11. loadBrandConfig() handles missing config gracefully
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
describe('loadBrandConfig()', () => {
|
|
245
|
+
beforeEach(() => {
|
|
246
|
+
vi.restoreAllMocks()
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
it('returns fallback config when fetch fails (network error)', async () => {
|
|
250
|
+
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('Network error')))
|
|
251
|
+
|
|
252
|
+
const config = await loadBrandConfig()
|
|
253
|
+
|
|
254
|
+
expect(config).toBeDefined()
|
|
255
|
+
expect(config.brand).toEqual({})
|
|
256
|
+
expect(config.chains).toBeDefined()
|
|
257
|
+
expect(config.rpc).toEqual({})
|
|
258
|
+
expect(config.api).toBeDefined()
|
|
259
|
+
expect(config.walletConnect).toBeDefined()
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('returns fallback config when fetch returns non-ok status', async () => {
|
|
263
|
+
vi.stubGlobal(
|
|
264
|
+
'fetch',
|
|
265
|
+
vi.fn().mockResolvedValue({
|
|
266
|
+
ok: false,
|
|
267
|
+
status: 404,
|
|
268
|
+
}),
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
const config = await loadBrandConfig()
|
|
272
|
+
|
|
273
|
+
expect(config).toBeDefined()
|
|
274
|
+
expect(config.brand).toEqual({})
|
|
275
|
+
expect(config.rpc).toEqual({})
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
it('applies brand overrides from config.json', async () => {
|
|
279
|
+
vi.stubGlobal(
|
|
280
|
+
'fetch',
|
|
281
|
+
vi.fn().mockResolvedValue({
|
|
282
|
+
ok: true,
|
|
283
|
+
json: async () => ({
|
|
284
|
+
brand: { name: 'Zoo Exchange', legalEntity: 'Zoo Labs Foundation' },
|
|
285
|
+
chains: { defaultChainId: 200200, supported: [200200] },
|
|
286
|
+
rpc: { '200200': 'https://api.zoo.network/rpc' },
|
|
287
|
+
api: { graphql: '', gateway: '', insights: '' },
|
|
288
|
+
walletConnect: { projectId: 'test-id' },
|
|
289
|
+
}),
|
|
290
|
+
}),
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
const config = await loadBrandConfig()
|
|
294
|
+
|
|
295
|
+
expect(config.brand?.name).toBe('Zoo Exchange')
|
|
296
|
+
expect(config.brand?.legalEntity).toBe('Zoo Labs Foundation')
|
|
297
|
+
expect(brand.name).toBe('Zoo Exchange')
|
|
298
|
+
expect(brand.legalEntity).toBe('Zoo Labs Foundation')
|
|
299
|
+
expect(brand.defaultChainId).toBe(200200)
|
|
300
|
+
expect(brand.walletConnectProjectId).toBe('test-id')
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
it('preserves default values for fields not in config.json', async () => {
|
|
304
|
+
vi.stubGlobal(
|
|
305
|
+
'fetch',
|
|
306
|
+
vi.fn().mockResolvedValue({
|
|
307
|
+
ok: true,
|
|
308
|
+
json: async () => ({
|
|
309
|
+
brand: { name: 'Partial Brand' },
|
|
310
|
+
chains: { defaultChainId: 1, supported: [] },
|
|
311
|
+
rpc: {},
|
|
312
|
+
api: { graphql: '', gateway: '', insights: '' },
|
|
313
|
+
walletConnect: { projectId: '' },
|
|
314
|
+
}),
|
|
315
|
+
}),
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
await loadBrandConfig()
|
|
319
|
+
|
|
320
|
+
expect(brand.name).toBe('Partial Brand')
|
|
321
|
+
expect(brand.faviconUrl).toBe('/favicon.ico')
|
|
322
|
+
expect(brand.primaryColor).toBe('#FC72FF')
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
it('derives walletName from name when not explicitly set', async () => {
|
|
326
|
+
vi.stubGlobal(
|
|
327
|
+
'fetch',
|
|
328
|
+
vi.fn().mockResolvedValue({
|
|
329
|
+
ok: true,
|
|
330
|
+
json: async () => ({
|
|
331
|
+
brand: { name: 'Zoo Exchange' },
|
|
332
|
+
chains: { defaultChainId: 200200, supported: [200200] },
|
|
333
|
+
rpc: {},
|
|
334
|
+
api: { graphql: '', gateway: '', insights: '' },
|
|
335
|
+
walletConnect: { projectId: '' },
|
|
336
|
+
}),
|
|
337
|
+
}),
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
await loadBrandConfig()
|
|
341
|
+
|
|
342
|
+
expect(brand.walletName).toBe('Zoo Wallet')
|
|
343
|
+
expect(brand.protocolName).toBe('Zoo Protocol')
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
it('uses explicit walletName over derived value', async () => {
|
|
347
|
+
vi.stubGlobal(
|
|
348
|
+
'fetch',
|
|
349
|
+
vi.fn().mockResolvedValue({
|
|
350
|
+
ok: true,
|
|
351
|
+
json: async () => ({
|
|
352
|
+
brand: { name: 'Zoo Exchange', walletName: 'Custom Wallet' },
|
|
353
|
+
chains: { defaultChainId: 200200, supported: [200200] },
|
|
354
|
+
rpc: {},
|
|
355
|
+
api: { graphql: '', gateway: '', insights: '' },
|
|
356
|
+
walletConnect: { projectId: '' },
|
|
357
|
+
}),
|
|
358
|
+
}),
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
await loadBrandConfig()
|
|
362
|
+
|
|
363
|
+
expect(brand.walletName).toBe('Custom Wallet')
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
it('derives copyrightHolder from legalEntity when not set', async () => {
|
|
367
|
+
vi.stubGlobal(
|
|
368
|
+
'fetch',
|
|
369
|
+
vi.fn().mockResolvedValue({
|
|
370
|
+
ok: true,
|
|
371
|
+
json: async () => ({
|
|
372
|
+
brand: { name: 'Zoo Exchange', legalEntity: 'Zoo Labs Foundation' },
|
|
373
|
+
chains: { defaultChainId: 200200, supported: [200200] },
|
|
374
|
+
rpc: {},
|
|
375
|
+
api: { graphql: '', gateway: '', insights: '' },
|
|
376
|
+
walletConnect: { projectId: '' },
|
|
377
|
+
}),
|
|
378
|
+
}),
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
await loadBrandConfig()
|
|
382
|
+
|
|
383
|
+
expect(brand.copyrightHolder).toBe('Zoo Labs Foundation')
|
|
384
|
+
})
|
|
385
|
+
})
|
package/env.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** biome-ignore-all lint/style/noNamespace: required to define process.env type */
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
namespace NodeJS {
|
|
5
|
+
// All process.env values used by this package should be listed here
|
|
6
|
+
interface ProcessEnv {
|
|
7
|
+
NODE_ENV?: 'development' | 'production' | 'test'
|
|
8
|
+
ALCHEMY_API_KEY?: string
|
|
9
|
+
AMPLITUDE_PROXY_URL_OVERRIDE?: string
|
|
10
|
+
API_BASE_URL_OVERRIDE?: string
|
|
11
|
+
API_BASE_URL_V2_OVERRIDE?: string
|
|
12
|
+
APPSFLYER_API_KEY?: string
|
|
13
|
+
APPSFLYER_APP_ID?: string
|
|
14
|
+
BLOCKAID_PROXY_URL?: string
|
|
15
|
+
CI?: string
|
|
16
|
+
DATADOG_CLIENT_TOKEN?: string
|
|
17
|
+
DATADOG_PROJECT_ID?: string
|
|
18
|
+
ENABLE_ENTRY_GATEWAY_PROXY?: string
|
|
19
|
+
ENABLE_SESSION_SERVICE?: string
|
|
20
|
+
ENABLE_SESSION_UPGRADE_AUTO?: string
|
|
21
|
+
ENTRY_GATEWAY_API_URL_OVERRIDE?: string
|
|
22
|
+
FOR_API_URL_OVERRIDE?: string
|
|
23
|
+
GRAPHQL_URL_OVERRIDE?: string
|
|
24
|
+
INCLUDE_PROTOTYPE_FEATURES?: string
|
|
25
|
+
IS_E2E_TEST?: string
|
|
26
|
+
JUPITER_PROXY_URL?: string
|
|
27
|
+
LIQUIDITY_SERVICE_URL_OVERRIDE?: string
|
|
28
|
+
ONESIGNAL_APP_ID?: string
|
|
29
|
+
QUICKNODE_ENDPOINT_NAME?: string
|
|
30
|
+
QUICKNODE_ENDPOINT_TOKEN?: string
|
|
31
|
+
REACT_APP_ALCHEMY_API_KEY?: string
|
|
32
|
+
REACT_APP_BLOCKAID_PROXY_URL?: string
|
|
33
|
+
REACT_APP_DATADOG_CLIENT_TOKEN?: string
|
|
34
|
+
REACT_APP_DATADOG_PROJECT_ID?: string
|
|
35
|
+
REACT_APP_ENABLE_SESSION_UPGRADE_AUTO?: string
|
|
36
|
+
REACT_APP_INFURA_KEY?: string
|
|
37
|
+
REACT_APP_JUPITER_PROXY_URL?: string
|
|
38
|
+
REACT_APP_LIQUIDITY_SERVICE_URL_OVERRIDE?: string
|
|
39
|
+
REACT_APP_QUICKNODE_ENDPOINT_NAME?: string
|
|
40
|
+
REACT_APP_QUICKNODE_ENDPOINT_TOKEN?: string
|
|
41
|
+
REACT_APP_STATSIG_API_KEY?: string
|
|
42
|
+
REACT_APP_TRADING_API_KEY?: string
|
|
43
|
+
REACT_APP_TRADING_API_TEST_ENV?: string
|
|
44
|
+
REACT_APP_TRADING_API_URL_OVERRIDE?: string
|
|
45
|
+
REACT_APP_WALLET_CONNECT_PROJECT_ID?: string
|
|
46
|
+
SCANTASTIC_API_URL_OVERRIDE?: string
|
|
47
|
+
STATSIG_API_KEY?: string
|
|
48
|
+
STATSIG_PROXY_URL_OVERRIDE?: string
|
|
49
|
+
TRADING_API_KEY?: string
|
|
50
|
+
TRADING_API_URL_OVERRIDE?: string
|
|
51
|
+
LX_API_KEY?: string
|
|
52
|
+
LX_NOTIF_API_BASE_URL_OVERRIDE?: string
|
|
53
|
+
UNITAGS_API_URL_OVERRIDE?: string
|
|
54
|
+
VERSION?: string
|
|
55
|
+
VERCEL?: string
|
|
56
|
+
VITE_ENABLE_ENTRY_GATEWAY_PROXY?: string
|
|
57
|
+
WALLETCONNECT_PROJECT_ID?: string
|
|
58
|
+
WALLETCONNECT_PROJECT_ID_BETA?: string
|
|
59
|
+
WALLETCONNECT_PROJECT_ID_DEV?: string
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export {}
|
package/env.native.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Adds typing for react-native-dotenv
|
|
2
|
+
// Must be kept separately from env.d.ts as due to issue with global
|
|
3
|
+
// namespace vs module declarations
|
|
4
|
+
declare module 'react-native-dotenv' {
|
|
5
|
+
export const ALCHEMY_API_KEY: string
|
|
6
|
+
export const AMPLITUDE_PROXY_URL_OVERRIDE: string
|
|
7
|
+
export const API_BASE_URL_OVERRIDE: string
|
|
8
|
+
export const API_BASE_URL_V2_OVERRIDE: string
|
|
9
|
+
export const APPSFLYER_API_KEY: string
|
|
10
|
+
export const APPSFLYER_APP_ID: string
|
|
11
|
+
export const BLOCKAID_PROXY_URL: string
|
|
12
|
+
export const DATADOG_CLIENT_TOKEN: string
|
|
13
|
+
export const DATADOG_PROJECT_ID: string
|
|
14
|
+
export const ENABLE_ENTRY_GATEWAY_PROXY: string
|
|
15
|
+
export const ENABLE_SESSION_SERVICE: string
|
|
16
|
+
export const ENABLE_SESSION_UPGRADE_AUTO: string
|
|
17
|
+
export const ENTRY_GATEWAY_API_URL_OVERRIDE: string
|
|
18
|
+
export const FOR_API_URL_OVERRIDE: string
|
|
19
|
+
export const GRAPHQL_URL_OVERRIDE: string
|
|
20
|
+
export const INCLUDE_PROTOTYPE_FEATURES: string
|
|
21
|
+
export const INFURA_KEY: string
|
|
22
|
+
export const IS_E2E_TEST: string
|
|
23
|
+
export const JUPITER_PROXY_URL: string
|
|
24
|
+
export const LIQUIDITY_SERVICE_URL_OVERRIDE: string
|
|
25
|
+
export const ONESIGNAL_APP_ID: string
|
|
26
|
+
export const QUICKNODE_ENDPOINT_NAME: string
|
|
27
|
+
export const QUICKNODE_ENDPOINT_TOKEN: string
|
|
28
|
+
export const SCANTASTIC_API_URL_OVERRIDE: string
|
|
29
|
+
export const STATSIG_API_KEY: string
|
|
30
|
+
export const STATSIG_PROXY_URL_OVERRIDE: string
|
|
31
|
+
export const TRADING_API_KEY: string
|
|
32
|
+
export const TRADING_API_URL_OVERRIDE: string
|
|
33
|
+
export const LX_API_KEY: string
|
|
34
|
+
export const LX_NOTIF_API_BASE_URL_OVERRIDE: string
|
|
35
|
+
export const UNITAGS_API_URL_OVERRIDE: string
|
|
36
|
+
export const WALLETCONNECT_PROJECT_ID: string
|
|
37
|
+
export const WALLETCONNECT_PROJECT_ID_BETA: string
|
|
38
|
+
export const WALLETCONNECT_PROJECT_ID_DEV: string
|
|
39
|
+
}
|