@claritydao/midnight-agora-sdk 0.1.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.
- package/.prettierrc +1 -0
- package/LICENSE +0 -0
- package/README.md +1134 -0
- package/dist/browser-providers.d.ts +21 -0
- package/dist/browser-providers.d.ts.map +1 -0
- package/dist/browser-providers.js +147 -0
- package/dist/browser-providers.js.map +1 -0
- package/dist/common-types.d.ts +29 -0
- package/dist/common-types.d.ts.map +1 -0
- package/dist/common-types.js +2 -0
- package/dist/common-types.js.map +1 -0
- package/dist/errors.d.ts +67 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +108 -0
- package/dist/errors.js.map +1 -0
- package/dist/governor-api.d.ts +348 -0
- package/dist/governor-api.d.ts.map +1 -0
- package/dist/governor-api.js +716 -0
- package/dist/governor-api.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/manager.d.ts +87 -0
- package/dist/manager.d.ts.map +1 -0
- package/dist/manager.js +93 -0
- package/dist/manager.js.map +1 -0
- package/dist/types.d.ts +307 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +77 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/index.d.ts +34 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +57 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/validators.d.ts +52 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +160 -0
- package/dist/validators.js.map +1 -0
- package/eslint.config.mjs +48 -0
- package/package.json +39 -0
- package/src/browser-providers.ts +274 -0
- package/src/common-types.ts +51 -0
- package/src/errors.ts +124 -0
- package/src/governor-api.ts +948 -0
- package/src/index.ts +51 -0
- package/src/manager.ts +203 -0
- package/src/types.ts +403 -0
- package/src/utils/index.ts +60 -0
- package/src/validators.ts +245 -0
- package/test/README.md +409 -0
- package/test/errors.test.ts +179 -0
- package/test/integration/governor-api.test.ts +370 -0
- package/test/mocks/providers.ts +130 -0
- package/test/types.test.ts +112 -0
- package/test/utils.test.ts +111 -0
- package/test/validators.test.ts +368 -0
- package/test/wasm-init.test.ts +132 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for production-ready type system
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
VoteChoice,
|
|
8
|
+
ProposalStatus,
|
|
9
|
+
ConfigPresets,
|
|
10
|
+
type DeploymentConfig,
|
|
11
|
+
type GovernanceConfig,
|
|
12
|
+
type TokenConfig,
|
|
13
|
+
type ProposalAction,
|
|
14
|
+
} from '../src/index';
|
|
15
|
+
|
|
16
|
+
describe('Type System', () => {
|
|
17
|
+
describe('VoteChoice Enum', () => {
|
|
18
|
+
it('should have correct vote choice values', () => {
|
|
19
|
+
expect(VoteChoice.For).toBe(0);
|
|
20
|
+
expect(VoteChoice.Against).toBe(1);
|
|
21
|
+
expect(VoteChoice.Abstain).toBe(2);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should be type-safe', () => {
|
|
25
|
+
const choice: VoteChoice = VoteChoice.For;
|
|
26
|
+
expect(choice).toBe(0);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('ProposalStatus Enum', () => {
|
|
31
|
+
it('should have all status values', () => {
|
|
32
|
+
expect(ProposalStatus.Pending).toBe(0);
|
|
33
|
+
expect(ProposalStatus.Active).toBe(1);
|
|
34
|
+
expect(ProposalStatus.Succeeded).toBe(2);
|
|
35
|
+
expect(ProposalStatus.Defeated).toBe(3);
|
|
36
|
+
expect(ProposalStatus.Queued).toBe(4);
|
|
37
|
+
expect(ProposalStatus.Executed).toBe(5);
|
|
38
|
+
expect(ProposalStatus.Expired).toBe(6);
|
|
39
|
+
expect(ProposalStatus.Canceled).toBe(7);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('ConfigPresets', () => {
|
|
44
|
+
it('should provide testing configuration', () => {
|
|
45
|
+
const config = ConfigPresets.testing();
|
|
46
|
+
|
|
47
|
+
expect(config.votingPeriod).toBe(BigInt(300)); // 5 minutes
|
|
48
|
+
expect(config.quorumThreshold).toBe(BigInt(100));
|
|
49
|
+
expect(config.proposalThreshold).toBe(BigInt(100));
|
|
50
|
+
expect(config.minStakeAmount).toBe(BigInt(10));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should provide production configuration', () => {
|
|
54
|
+
const config = ConfigPresets.production();
|
|
55
|
+
|
|
56
|
+
expect(config.votingPeriod).toBe(BigInt(604800)); // 7 days
|
|
57
|
+
expect(config.quorumThreshold).toBe(BigInt(100000));
|
|
58
|
+
expect(config.proposalThreshold).toBe(BigInt(50000));
|
|
59
|
+
expect(config.minStakeAmount).toBe(BigInt(1000));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('testing config should have shorter periods than production', () => {
|
|
63
|
+
const testing = ConfigPresets.testing();
|
|
64
|
+
const production = ConfigPresets.production();
|
|
65
|
+
|
|
66
|
+
expect(testing.votingPeriod < production.votingPeriod).toBe(true);
|
|
67
|
+
expect(testing.quorumThreshold < production.quorumThreshold).toBe(true);
|
|
68
|
+
expect(testing.proposalThreshold < production.proposalThreshold).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('Type Definitions', () => {
|
|
73
|
+
it('should have valid DeploymentConfig structure', () => {
|
|
74
|
+
const config: DeploymentConfig = {
|
|
75
|
+
governor: {
|
|
76
|
+
governance: ConfigPresets.testing(),
|
|
77
|
+
executionDelay: BigInt(60),
|
|
78
|
+
gracePeriod: BigInt(300),
|
|
79
|
+
maxActiveProposals: BigInt(100),
|
|
80
|
+
proposalLifetime: BigInt(3600),
|
|
81
|
+
emergencyVetoEnabled: true,
|
|
82
|
+
emergencyVetoThreshold: BigInt(75000),
|
|
83
|
+
},
|
|
84
|
+
token: {
|
|
85
|
+
name: 'Test DAO',
|
|
86
|
+
symbol: 'TDAO',
|
|
87
|
+
decimals: BigInt(18),
|
|
88
|
+
},
|
|
89
|
+
adminKey: new Uint8Array(32),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
expect(config.governor.governance).toBeDefined();
|
|
93
|
+
expect(config.token.name).toBe('Test DAO');
|
|
94
|
+
expect(config.adminKey.length).toBe(32);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should have valid ProposalAction structure', () => {
|
|
98
|
+
const action: ProposalAction = {
|
|
99
|
+
typ: 0,
|
|
100
|
+
token: new Uint8Array(32),
|
|
101
|
+
amount: BigInt(1000),
|
|
102
|
+
to: new Uint8Array(32),
|
|
103
|
+
configKey: new Uint8Array(32),
|
|
104
|
+
configValue: new Uint8Array(32),
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
expect(action.typ).toBe(0);
|
|
108
|
+
expect(action.amount).toBe(BigInt(1000));
|
|
109
|
+
expect(action.token.length).toBe(32);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for utility functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import { randomBytes, addressToBytes, bytesToHex, hexToBytes } from '../src/utils';
|
|
7
|
+
|
|
8
|
+
describe('Utility Functions', () => {
|
|
9
|
+
describe('randomBytes', () => {
|
|
10
|
+
it('should generate correct length', () => {
|
|
11
|
+
const bytes = randomBytes(32);
|
|
12
|
+
expect(bytes.length).toBe(32);
|
|
13
|
+
expect(bytes).toBeInstanceOf(Uint8Array);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should generate different values each time', () => {
|
|
17
|
+
const bytes1 = randomBytes(32);
|
|
18
|
+
const bytes2 = randomBytes(32);
|
|
19
|
+
|
|
20
|
+
// Extremely unlikely to be equal
|
|
21
|
+
expect(bytes1).not.toEqual(bytes2);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should generate non-zero values', () => {
|
|
25
|
+
const bytes = randomBytes(32);
|
|
26
|
+
const hasNonZero = Array.from(bytes).some(b => b !== 0);
|
|
27
|
+
expect(hasNonZero).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('bytesToHex', () => {
|
|
32
|
+
it('should convert bytes to hex string', () => {
|
|
33
|
+
const bytes = new Uint8Array([0, 1, 15, 255]);
|
|
34
|
+
const hex = bytesToHex(bytes);
|
|
35
|
+
expect(hex).toBe('00010fff');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should handle empty array', () => {
|
|
39
|
+
const bytes = new Uint8Array([]);
|
|
40
|
+
const hex = bytesToHex(bytes);
|
|
41
|
+
expect(hex).toBe('');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should pad single digits with zero', () => {
|
|
45
|
+
const bytes = new Uint8Array([0, 1, 2, 15]);
|
|
46
|
+
const hex = bytesToHex(bytes);
|
|
47
|
+
expect(hex).toBe('0001020f');
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('hexToBytes', () => {
|
|
52
|
+
it('should convert hex string to bytes', () => {
|
|
53
|
+
const hex = '00010fff';
|
|
54
|
+
const bytes = hexToBytes(hex);
|
|
55
|
+
expect(bytes).toEqual(new Uint8Array([0, 1, 15, 255]));
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should handle 0x prefix', () => {
|
|
59
|
+
const hex = '0x00010fff';
|
|
60
|
+
const bytes = hexToBytes(hex);
|
|
61
|
+
expect(bytes).toEqual(new Uint8Array([0, 1, 15, 255]));
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should handle empty string', () => {
|
|
65
|
+
const hex = '';
|
|
66
|
+
const bytes = hexToBytes(hex);
|
|
67
|
+
expect(bytes).toEqual(new Uint8Array([]));
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('bytesToHex and hexToBytes round-trip', () => {
|
|
72
|
+
it('should be reversible', () => {
|
|
73
|
+
const original = new Uint8Array([0, 1, 15, 127, 255]);
|
|
74
|
+
const hex = bytesToHex(original);
|
|
75
|
+
const decoded = hexToBytes(hex);
|
|
76
|
+
expect(decoded).toEqual(original);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should work with random bytes', () => {
|
|
80
|
+
const original = randomBytes(32);
|
|
81
|
+
const hex = bytesToHex(original);
|
|
82
|
+
const decoded = hexToBytes(hex);
|
|
83
|
+
expect(decoded).toEqual(original);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('addressToBytes', () => {
|
|
88
|
+
it('should convert address string to 32 bytes', () => {
|
|
89
|
+
const address = 'mn_shield-addr_test1n3axpjpcwj8fjutdfqa9jnum8x7zvu8u7382pw4u99zax3qwdndqxq85jv7yda6yls72sqsgx8hqkvr7fzatfykypp7au3ye9qrysa3wru5e4wz9';
|
|
90
|
+
const bytes = addressToBytes(address);
|
|
91
|
+
|
|
92
|
+
expect(bytes).toBeInstanceOf(Uint8Array);
|
|
93
|
+
expect(bytes.length).toBe(32);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should handle addresses without prefix', () => {
|
|
97
|
+
const address = 'abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890';
|
|
98
|
+
const bytes = addressToBytes(address);
|
|
99
|
+
|
|
100
|
+
expect(bytes.length).toBe(32);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should extract last 64 hex chars', () => {
|
|
104
|
+
const address = 'prefix_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
|
|
105
|
+
const bytes = addressToBytes(address);
|
|
106
|
+
|
|
107
|
+
// Should extract the 64 hex chars (32 bytes)
|
|
108
|
+
expect(bytes.length).toBe(32);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for validation functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import { validators } from '../src/index';
|
|
7
|
+
import { VoteChoice, ConfigPresets } from '../src/index';
|
|
8
|
+
import {
|
|
9
|
+
InvalidParameterError,
|
|
10
|
+
InvalidConfigurationError,
|
|
11
|
+
} from '../src/index';
|
|
12
|
+
|
|
13
|
+
describe('Validators', () => {
|
|
14
|
+
describe('validateProposal', () => {
|
|
15
|
+
it('should accept valid proposal parameters', () => {
|
|
16
|
+
const title = 'Valid Proposal Title';
|
|
17
|
+
const description = 'This is a valid proposal description';
|
|
18
|
+
const creator = new Uint8Array(32);
|
|
19
|
+
|
|
20
|
+
expect(() => {
|
|
21
|
+
validators.validateProposal(title, description, creator);
|
|
22
|
+
}).not.toThrow();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should reject empty title', () => {
|
|
26
|
+
const title = '';
|
|
27
|
+
const description = 'Valid description';
|
|
28
|
+
const creator = new Uint8Array(32);
|
|
29
|
+
|
|
30
|
+
expect(() => {
|
|
31
|
+
validators.validateProposal(title, description, creator);
|
|
32
|
+
}).toThrow(InvalidParameterError);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should reject title that is too long', () => {
|
|
36
|
+
const title = 'a'.repeat(257); // Max is 256
|
|
37
|
+
const description = 'Valid description';
|
|
38
|
+
const creator = new Uint8Array(32);
|
|
39
|
+
|
|
40
|
+
expect(() => {
|
|
41
|
+
validators.validateProposal(title, description, creator);
|
|
42
|
+
}).toThrow(InvalidParameterError);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should reject empty description', () => {
|
|
46
|
+
const title = 'Valid title';
|
|
47
|
+
const description = '';
|
|
48
|
+
const creator = new Uint8Array(32);
|
|
49
|
+
|
|
50
|
+
expect(() => {
|
|
51
|
+
validators.validateProposal(title, description, creator);
|
|
52
|
+
}).toThrow(InvalidParameterError);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should reject description that is too long', () => {
|
|
56
|
+
const title = 'Valid title';
|
|
57
|
+
const description = 'a'.repeat(10001); // Max is 10000
|
|
58
|
+
const creator = new Uint8Array(32);
|
|
59
|
+
|
|
60
|
+
expect(() => {
|
|
61
|
+
validators.validateProposal(title, description, creator);
|
|
62
|
+
}).toThrow(InvalidParameterError);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should reject invalid creator address (not 32 bytes)', () => {
|
|
66
|
+
const title = 'Valid title';
|
|
67
|
+
const description = 'Valid description';
|
|
68
|
+
const creator = new Uint8Array(16); // Should be 32
|
|
69
|
+
|
|
70
|
+
expect(() => {
|
|
71
|
+
validators.validateProposal(title, description, creator);
|
|
72
|
+
}).toThrow(InvalidParameterError);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should reject non-Uint8Array creator', () => {
|
|
76
|
+
const title = 'Valid title';
|
|
77
|
+
const description = 'Valid description';
|
|
78
|
+
const creator = [1, 2, 3] as any;
|
|
79
|
+
|
|
80
|
+
expect(() => {
|
|
81
|
+
validators.validateProposal(title, description, creator);
|
|
82
|
+
}).toThrow(InvalidParameterError);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('validateVoteChoice', () => {
|
|
87
|
+
it('should accept VoteChoice.For', () => {
|
|
88
|
+
expect(() => {
|
|
89
|
+
validators.validateVoteChoice(VoteChoice.For);
|
|
90
|
+
}).not.toThrow();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should accept VoteChoice.Against', () => {
|
|
94
|
+
expect(() => {
|
|
95
|
+
validators.validateVoteChoice(VoteChoice.Against);
|
|
96
|
+
}).not.toThrow();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should accept VoteChoice.Abstain', () => {
|
|
100
|
+
expect(() => {
|
|
101
|
+
validators.validateVoteChoice(VoteChoice.Abstain);
|
|
102
|
+
}).not.toThrow();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should reject invalid vote choice (3)', () => {
|
|
106
|
+
expect(() => {
|
|
107
|
+
validators.validateVoteChoice(3 as any);
|
|
108
|
+
}).toThrow(InvalidParameterError);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should reject invalid vote choice (-1)', () => {
|
|
112
|
+
expect(() => {
|
|
113
|
+
validators.validateVoteChoice(-1 as any);
|
|
114
|
+
}).toThrow(InvalidParameterError);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('validateAddress', () => {
|
|
119
|
+
it('should accept valid 32-byte address', () => {
|
|
120
|
+
const address = new Uint8Array(32);
|
|
121
|
+
|
|
122
|
+
expect(() => {
|
|
123
|
+
validators.validateAddress(address, 'testAddress');
|
|
124
|
+
}).not.toThrow();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('should reject address that is too short', () => {
|
|
128
|
+
const address = new Uint8Array(31);
|
|
129
|
+
|
|
130
|
+
expect(() => {
|
|
131
|
+
validators.validateAddress(address, 'testAddress');
|
|
132
|
+
}).toThrow(InvalidParameterError);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should reject address that is too long', () => {
|
|
136
|
+
const address = new Uint8Array(33);
|
|
137
|
+
|
|
138
|
+
expect(() => {
|
|
139
|
+
validators.validateAddress(address, 'testAddress');
|
|
140
|
+
}).toThrow(InvalidParameterError);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should reject non-Uint8Array', () => {
|
|
144
|
+
const address = 'not-a-uint8array' as any;
|
|
145
|
+
|
|
146
|
+
expect(() => {
|
|
147
|
+
validators.validateAddress(address, 'testAddress');
|
|
148
|
+
}).toThrow(InvalidParameterError);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('validateGovernanceConfig', () => {
|
|
153
|
+
it('should accept valid testing config', () => {
|
|
154
|
+
const config = ConfigPresets.testing();
|
|
155
|
+
|
|
156
|
+
expect(() => {
|
|
157
|
+
validators.validateGovernanceConfig(config);
|
|
158
|
+
}).not.toThrow();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should accept valid production config', () => {
|
|
162
|
+
const config = ConfigPresets.production();
|
|
163
|
+
|
|
164
|
+
expect(() => {
|
|
165
|
+
validators.validateGovernanceConfig(config);
|
|
166
|
+
}).not.toThrow();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should reject config with votingPeriod less than 60', () => {
|
|
170
|
+
const config = {
|
|
171
|
+
...ConfigPresets.testing(),
|
|
172
|
+
votingPeriod: BigInt(59), // Less than minimum
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
expect(() => {
|
|
176
|
+
validators.validateGovernanceConfig(config);
|
|
177
|
+
}).toThrow(InvalidConfigurationError);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should reject config where proposalThreshold > quorumThreshold', () => {
|
|
181
|
+
const config = {
|
|
182
|
+
...ConfigPresets.testing(),
|
|
183
|
+
proposalThreshold: BigInt(200),
|
|
184
|
+
quorumThreshold: BigInt(100), // Should be >= proposalThreshold
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
expect(() => {
|
|
188
|
+
validators.validateGovernanceConfig(config);
|
|
189
|
+
}).toThrow(InvalidConfigurationError);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should reject negative values', () => {
|
|
193
|
+
const config = {
|
|
194
|
+
...ConfigPresets.testing(),
|
|
195
|
+
executionDelay: BigInt(-1),
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
expect(() => {
|
|
199
|
+
validators.validateGovernanceConfig(config);
|
|
200
|
+
}).toThrow(InvalidParameterError);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe('validateTokenConfig', () => {
|
|
205
|
+
it('should accept valid token config', () => {
|
|
206
|
+
const config = {
|
|
207
|
+
name: 'My DAO Token',
|
|
208
|
+
symbol: 'MDAO',
|
|
209
|
+
decimals: BigInt(18),
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
expect(() => {
|
|
213
|
+
validators.validateTokenConfig(config);
|
|
214
|
+
}).not.toThrow();
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should reject empty token name', () => {
|
|
218
|
+
const config = {
|
|
219
|
+
name: '',
|
|
220
|
+
symbol: 'MDAO',
|
|
221
|
+
decimals: BigInt(18),
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
expect(() => {
|
|
225
|
+
validators.validateTokenConfig(config);
|
|
226
|
+
}).toThrow(InvalidParameterError);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('should reject token name that is too long', () => {
|
|
230
|
+
const config = {
|
|
231
|
+
name: 'a'.repeat(65), // Max is 64
|
|
232
|
+
symbol: 'MDAO',
|
|
233
|
+
decimals: BigInt(18),
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
expect(() => {
|
|
237
|
+
validators.validateTokenConfig(config);
|
|
238
|
+
}).toThrow(InvalidParameterError);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should reject empty token symbol', () => {
|
|
242
|
+
const config = {
|
|
243
|
+
name: 'My DAO Token',
|
|
244
|
+
symbol: '',
|
|
245
|
+
decimals: BigInt(18),
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
expect(() => {
|
|
249
|
+
validators.validateTokenConfig(config);
|
|
250
|
+
}).toThrow(InvalidParameterError);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should reject token symbol that is too long', () => {
|
|
254
|
+
const config = {
|
|
255
|
+
name: 'My DAO Token',
|
|
256
|
+
symbol: 'a'.repeat(17), // Max is 16
|
|
257
|
+
decimals: BigInt(18),
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
expect(() => {
|
|
261
|
+
validators.validateTokenConfig(config);
|
|
262
|
+
}).toThrow(InvalidParameterError);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('should reject decimals greater than 18', () => {
|
|
266
|
+
const config = {
|
|
267
|
+
name: 'My DAO Token',
|
|
268
|
+
symbol: 'MDAO',
|
|
269
|
+
decimals: BigInt(19),
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
expect(() => {
|
|
273
|
+
validators.validateTokenConfig(config);
|
|
274
|
+
}).toThrow(InvalidConfigurationError);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('should reject zero decimals', () => {
|
|
278
|
+
const config = {
|
|
279
|
+
name: 'My DAO Token',
|
|
280
|
+
symbol: 'MDAO',
|
|
281
|
+
decimals: BigInt(0),
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
expect(() => {
|
|
285
|
+
validators.validateTokenConfig(config);
|
|
286
|
+
}).toThrow(InvalidParameterError);
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
describe('validateDeploymentConfig', () => {
|
|
291
|
+
it('should accept valid deployment config', () => {
|
|
292
|
+
const governanceConfig = ConfigPresets.testing();
|
|
293
|
+
const config = {
|
|
294
|
+
governor: {
|
|
295
|
+
governance: governanceConfig,
|
|
296
|
+
executionDelay: governanceConfig.executionDelay,
|
|
297
|
+
gracePeriod: governanceConfig.gracePeriod,
|
|
298
|
+
maxActiveProposals: BigInt(100),
|
|
299
|
+
proposalLifetime: governanceConfig.proposalLifetime,
|
|
300
|
+
emergencyVetoEnabled: true,
|
|
301
|
+
emergencyVetoThreshold: BigInt(75000),
|
|
302
|
+
},
|
|
303
|
+
token: {
|
|
304
|
+
name: 'Test DAO',
|
|
305
|
+
symbol: 'TDAO',
|
|
306
|
+
decimals: BigInt(18),
|
|
307
|
+
},
|
|
308
|
+
adminKey: new Uint8Array(32),
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
expect(() => {
|
|
312
|
+
validators.validateDeploymentConfig(config);
|
|
313
|
+
}).not.toThrow();
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should reject config with invalid admin key length', () => {
|
|
317
|
+
const governanceConfig = ConfigPresets.testing();
|
|
318
|
+
const config = {
|
|
319
|
+
governor: {
|
|
320
|
+
governance: governanceConfig,
|
|
321
|
+
executionDelay: governanceConfig.executionDelay,
|
|
322
|
+
gracePeriod: governanceConfig.gracePeriod,
|
|
323
|
+
maxActiveProposals: BigInt(100),
|
|
324
|
+
proposalLifetime: governanceConfig.proposalLifetime,
|
|
325
|
+
emergencyVetoEnabled: true,
|
|
326
|
+
emergencyVetoThreshold: BigInt(75000),
|
|
327
|
+
},
|
|
328
|
+
token: {
|
|
329
|
+
name: 'Test DAO',
|
|
330
|
+
symbol: 'TDAO',
|
|
331
|
+
decimals: BigInt(18),
|
|
332
|
+
},
|
|
333
|
+
adminKey: new Uint8Array(16), // Should be 32
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
expect(() => {
|
|
337
|
+
validators.validateDeploymentConfig(config);
|
|
338
|
+
}).toThrow(InvalidParameterError);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('should reject config with inconsistent executionDelay', () => {
|
|
342
|
+
const config = {
|
|
343
|
+
governor: {
|
|
344
|
+
governance: {
|
|
345
|
+
...ConfigPresets.testing(),
|
|
346
|
+
executionDelay: BigInt(120),
|
|
347
|
+
},
|
|
348
|
+
executionDelay: BigInt(60), // Doesn't match governance.executionDelay
|
|
349
|
+
gracePeriod: BigInt(300),
|
|
350
|
+
maxActiveProposals: BigInt(100),
|
|
351
|
+
proposalLifetime: BigInt(3600),
|
|
352
|
+
emergencyVetoEnabled: true,
|
|
353
|
+
emergencyVetoThreshold: BigInt(75000),
|
|
354
|
+
},
|
|
355
|
+
token: {
|
|
356
|
+
name: 'Test DAO',
|
|
357
|
+
symbol: 'TDAO',
|
|
358
|
+
decimals: BigInt(18),
|
|
359
|
+
},
|
|
360
|
+
adminKey: new Uint8Array(32),
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
expect(() => {
|
|
364
|
+
validators.validateDeploymentConfig(config);
|
|
365
|
+
}).toThrow(InvalidConfigurationError);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
});
|