@openzeppelin/wizard-stellar 0.4.5 → 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.
- package/dist/add-pausable.d.ts.map +1 -1
- package/dist/add-pausable.js +0 -2
- package/dist/add-pausable.js.map +1 -1
- package/dist/add-upgradeable.d.ts.map +1 -1
- package/dist/add-upgradeable.js +15 -14
- package/dist/add-upgradeable.js.map +1 -1
- package/dist/api.d.ts +5 -2
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +7 -1
- package/dist/api.js.map +1 -1
- package/dist/build-generic.d.ts +4 -0
- package/dist/build-generic.d.ts.map +1 -1
- package/dist/build-generic.js +3 -0
- package/dist/build-generic.js.map +1 -1
- package/dist/contract.d.ts +3 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +4 -0
- package/dist/contract.js.map +1 -1
- package/dist/fungible.d.ts +1 -0
- package/dist/fungible.d.ts.map +1 -1
- package/dist/fungible.js +83 -40
- package/dist/fungible.js.map +1 -1
- package/dist/generate/fungible.d.ts.map +1 -1
- package/dist/generate/fungible.js +1 -0
- package/dist/generate/fungible.js.map +1 -1
- package/dist/generate/governor.d.ts +3 -0
- package/dist/generate/governor.d.ts.map +1 -0
- package/dist/generate/governor.js +24 -0
- package/dist/generate/governor.js.map +1 -0
- package/dist/generate/non-fungible.d.ts.map +1 -1
- package/dist/generate/non-fungible.js +1 -0
- package/dist/generate/non-fungible.js.map +1 -1
- package/dist/generate/sources.d.ts.map +1 -1
- package/dist/generate/sources.js +6 -0
- package/dist/generate/sources.js.map +1 -1
- package/dist/generate/stablecoin.d.ts.map +1 -1
- package/dist/generate/stablecoin.js +1 -0
- package/dist/generate/stablecoin.js.map +1 -1
- package/dist/governor.d.ts +17 -0
- package/dist/governor.d.ts.map +1 -0
- package/dist/governor.js +378 -0
- package/dist/governor.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/kind.js +1 -0
- package/dist/kind.js.map +1 -1
- package/dist/non-fungible.d.ts +1 -0
- package/dist/non-fungible.d.ts.map +1 -1
- package/dist/non-fungible.js +72 -22
- package/dist/non-fungible.js.map +1 -1
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +6 -1
- package/dist/print.js.map +1 -1
- package/dist/set-access-control.d.ts.map +1 -1
- package/dist/set-access-control.js +16 -10
- package/dist/set-access-control.js.map +1 -1
- package/dist/stablecoin.d.ts.map +1 -1
- package/dist/stablecoin.js +7 -0
- package/dist/stablecoin.js.map +1 -1
- package/dist/test.js +3 -0
- package/dist/test.js.map +1 -1
- package/dist/utils/compile-test.d.ts +2 -2
- package/dist/utils/compile-test.d.ts.map +1 -1
- package/dist/utils/compile-test.js +54 -3
- package/dist/utils/compile-test.js.map +1 -1
- package/dist/utils/version.d.ts +4 -4
- package/dist/utils/version.js +3 -3
- package/dist/zip-rust.d.ts.map +1 -1
- package/dist/zip-rust.js +3 -2
- package/dist/zip-rust.js.map +1 -1
- package/dist/zip-shared.d.ts +3 -4
- package/dist/zip-shared.d.ts.map +1 -1
- package/dist/zip-shared.js +20 -7
- package/dist/zip-shared.js.map +1 -1
- package/package.json +1 -1
- package/src/add-pausable.ts +0 -1
- package/src/add-upgradeable.ts +15 -16
- package/src/api.ts +15 -2
- package/src/build-generic.ts +6 -0
- package/src/contract.ts +6 -0
- package/src/fungible.ts +102 -40
- package/src/generate/fungible.ts +1 -0
- package/src/generate/governor.ts +24 -0
- package/src/generate/non-fungible.ts +1 -0
- package/src/generate/sources.ts +7 -0
- package/src/generate/stablecoin.ts +1 -0
- package/src/governor.ts +425 -0
- package/src/index.ts +2 -0
- package/src/kind.ts +1 -0
- package/src/non-fungible.ts +77 -14
- package/src/print.ts +6 -1
- package/src/set-access-control.ts +16 -10
- package/src/stablecoin.ts +8 -0
- package/src/test.ts +4 -0
- package/src/utils/compile-test.ts +84 -5
- package/src/utils/version.ts +3 -3
- package/src/zip-rust.ts +4 -2
- package/src/zip-shared.ts +17 -6
package/src/governor.ts
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import type { CommonContractOptions } from './common-options';
|
|
2
|
+
import { contractDefaults as commonDefaults, getSelfArg, withCommonContractDefaults } from './common-options';
|
|
3
|
+
import { ContractBuilder } from './contract';
|
|
4
|
+
import { printContract } from './print';
|
|
5
|
+
import { setAccessControl } from './set-access-control';
|
|
6
|
+
import { addUpgradeable } from './add-upgradeable';
|
|
7
|
+
import { setInfo } from './set-info';
|
|
8
|
+
import { toByteArray, toUint } from './utils/convert-strings';
|
|
9
|
+
import { defineFunctions } from './utils/define-functions';
|
|
10
|
+
|
|
11
|
+
export const defaults: Required<GovernorOptions> = {
|
|
12
|
+
name: 'MyGovernor',
|
|
13
|
+
version: '1.0.0',
|
|
14
|
+
votingDelay: '17000',
|
|
15
|
+
votingPeriod: '120000',
|
|
16
|
+
proposalThreshold: '100',
|
|
17
|
+
quorum: '500',
|
|
18
|
+
upgradeable: false,
|
|
19
|
+
timelock: false,
|
|
20
|
+
access: commonDefaults.access,
|
|
21
|
+
info: commonDefaults.info,
|
|
22
|
+
explicitImplementations: commonDefaults.explicitImplementations,
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
export function printGovernor(opts: GovernorOptions = defaults): string {
|
|
26
|
+
return printContract(buildGovernor(opts));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface GovernorOptions extends CommonContractOptions {
|
|
30
|
+
name: string;
|
|
31
|
+
version?: string;
|
|
32
|
+
votingDelay?: string;
|
|
33
|
+
votingPeriod?: string;
|
|
34
|
+
proposalThreshold?: string;
|
|
35
|
+
quorum?: string;
|
|
36
|
+
upgradeable?: boolean;
|
|
37
|
+
timelock?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function isAccessControlRequired(opts: Partial<GovernorOptions>): boolean {
|
|
41
|
+
return opts.upgradeable === true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function withDefaults(opts: GovernorOptions): Required<GovernorOptions> {
|
|
45
|
+
return {
|
|
46
|
+
...opts,
|
|
47
|
+
...withCommonContractDefaults(opts),
|
|
48
|
+
version: opts.version ?? defaults.version,
|
|
49
|
+
votingDelay: opts.votingDelay ?? defaults.votingDelay,
|
|
50
|
+
votingPeriod: opts.votingPeriod ?? defaults.votingPeriod,
|
|
51
|
+
proposalThreshold: opts.proposalThreshold ?? defaults.proposalThreshold,
|
|
52
|
+
quorum: opts.quorum ?? defaults.quorum,
|
|
53
|
+
upgradeable: opts.upgradeable ?? defaults.upgradeable,
|
|
54
|
+
timelock: opts.timelock ?? defaults.timelock,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function buildGovernor(opts: GovernorOptions): ContractBuilder {
|
|
59
|
+
const allOpts = withDefaults(opts);
|
|
60
|
+
const c = new ContractBuilder(allOpts.name);
|
|
61
|
+
|
|
62
|
+
addBase(
|
|
63
|
+
c,
|
|
64
|
+
toByteArray(allOpts.name),
|
|
65
|
+
toByteArray(allOpts.version),
|
|
66
|
+
toUint(allOpts.votingDelay, 'votingDelay', 'u32'),
|
|
67
|
+
toUint(allOpts.votingPeriod, 'votingPeriod', 'u32'),
|
|
68
|
+
toUint(allOpts.proposalThreshold, 'proposalThreshold', 'u128'),
|
|
69
|
+
toUint(allOpts.quorum, 'quorum', 'u128'),
|
|
70
|
+
allOpts.timelock,
|
|
71
|
+
allOpts.explicitImplementations,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (allOpts.upgradeable) {
|
|
75
|
+
addUpgradeable(c, allOpts.access, allOpts.explicitImplementations);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
setAccessControl(c, allOpts.access, allOpts.explicitImplementations);
|
|
79
|
+
setInfo(c, allOpts.info);
|
|
80
|
+
|
|
81
|
+
return c;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function addBase(
|
|
85
|
+
c: ContractBuilder,
|
|
86
|
+
name: string,
|
|
87
|
+
version: string,
|
|
88
|
+
votingDelay: bigint,
|
|
89
|
+
votingPeriod: bigint,
|
|
90
|
+
proposalThreshold: bigint,
|
|
91
|
+
quorum: bigint,
|
|
92
|
+
timelock: boolean,
|
|
93
|
+
explicitImplementations: boolean,
|
|
94
|
+
) {
|
|
95
|
+
c.addUseClause('stellar_governance::governor', 'Governor');
|
|
96
|
+
c.addUseClause('stellar_governance::governor', 'ProposalState');
|
|
97
|
+
c.addUseClause('stellar_governance::governor', 'self', { alias: 'governor' });
|
|
98
|
+
c.addUseClause('soroban_sdk', 'contract');
|
|
99
|
+
c.addUseClause('soroban_sdk', 'contractimpl');
|
|
100
|
+
c.addUseClause('soroban_sdk', 'Address');
|
|
101
|
+
c.addUseClause('soroban_sdk', 'BytesN');
|
|
102
|
+
c.addUseClause('soroban_sdk', 'Env');
|
|
103
|
+
c.addUseClause('soroban_sdk', 'String');
|
|
104
|
+
c.addUseClause('soroban_sdk', 'Symbol');
|
|
105
|
+
c.addUseClause('soroban_sdk', 'Val');
|
|
106
|
+
c.addUseClause('soroban_sdk', 'Vec');
|
|
107
|
+
|
|
108
|
+
c.addConstructorArgument({ name: 'token_contract', type: 'Address' });
|
|
109
|
+
c.addConstructorCode(`governor::set_name(e, String::from_str(e, "${name}"));`);
|
|
110
|
+
c.addConstructorCode(`governor::set_version(e, String::from_str(e, "${version}"));`);
|
|
111
|
+
c.addConstructorCode(`governor::set_voting_delay(e, ${votingDelay});`);
|
|
112
|
+
c.addConstructorCode(`governor::set_voting_period(e, ${votingPeriod});`);
|
|
113
|
+
c.addConstructorCode(`governor::set_proposal_threshold(e, ${proposalThreshold});`);
|
|
114
|
+
c.addConstructorCode(`governor::set_quorum(e, ${quorum});`);
|
|
115
|
+
c.addConstructorCode('governor::set_token_contract(e, &token_contract);');
|
|
116
|
+
|
|
117
|
+
const governorTrait = {
|
|
118
|
+
traitName: 'Governor',
|
|
119
|
+
structName: c.name,
|
|
120
|
+
tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Custom impls must be added before the defaults so the deduplication keeps
|
|
124
|
+
// the custom version when a default-method override exists (e.g. timelock's
|
|
125
|
+
// `proposals_need_queuing`/`queue` overriding the trait defaults).
|
|
126
|
+
if (timelock) {
|
|
127
|
+
addTimelock(c, governorTrait);
|
|
128
|
+
} else {
|
|
129
|
+
c.addTraitFunction(governorTrait, functions.execute);
|
|
130
|
+
c.addTraitFunction(governorTrait, functions.cancel);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (explicitImplementations) {
|
|
134
|
+
c.addTraitForEachFunctions(governorTrait, governorTraitDefaultFunctions);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function addTimelock(c: ContractBuilder, governorTrait: { traitName: string; structName: string; tags: string[] }) {
|
|
139
|
+
c.addUseClause('soroban_sdk', 'contracttype');
|
|
140
|
+
c.addUseClause('soroban_sdk', 'IntoVal');
|
|
141
|
+
c.addUseClause('stellar_governance::timelock', 'TimelockClient');
|
|
142
|
+
|
|
143
|
+
c.addTopLevelDeclaration([
|
|
144
|
+
'#[contracttype]',
|
|
145
|
+
'enum GovernorTimelockKey {',
|
|
146
|
+
' Timelock,',
|
|
147
|
+
' OperationID(BytesN<32>),',
|
|
148
|
+
'}',
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
c.addConstructorArgument({ name: 'timelock_contract', type: 'Address' });
|
|
152
|
+
c.addConstructorCode('e.storage().instance().set(&GovernorTimelockKey::Timelock, &timelock_contract);');
|
|
153
|
+
|
|
154
|
+
c.addFreeFunction(timelockFreeFunctions.timelock);
|
|
155
|
+
|
|
156
|
+
c.addTraitFunction(governorTrait, timelockFunctions.proposals_need_queuing);
|
|
157
|
+
c.addTraitFunction(governorTrait, timelockFunctions.queue);
|
|
158
|
+
c.addTraitFunction(governorTrait, timelockFunctions.execute);
|
|
159
|
+
c.addTraitFunction(governorTrait, timelockFunctions.cancel);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const functions = defineFunctions({
|
|
163
|
+
execute: {
|
|
164
|
+
args: [
|
|
165
|
+
getSelfArg(),
|
|
166
|
+
{ name: 'targets', type: 'Vec<Address>' },
|
|
167
|
+
{ name: 'functions', type: 'Vec<Symbol>' },
|
|
168
|
+
{ name: 'args', type: 'Vec<Vec<Val>>' },
|
|
169
|
+
{ name: 'description_hash', type: 'BytesN<32>' },
|
|
170
|
+
{ name: 'executor', type: 'Address' },
|
|
171
|
+
],
|
|
172
|
+
returns: 'BytesN<32>',
|
|
173
|
+
code: [
|
|
174
|
+
'executor.require_auth()',
|
|
175
|
+
'let proposal_id = governor::hash_proposal(e, &targets, &functions, &args, &description_hash)',
|
|
176
|
+
'let snapshot = governor::get_proposal_snapshot(e, &proposal_id)',
|
|
177
|
+
'let quorum = Self::quorum(e, snapshot)',
|
|
178
|
+
'governor::execute(e, targets, functions, args, &description_hash, Self::proposals_need_queuing(e), quorum)',
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
cancel: {
|
|
182
|
+
args: [
|
|
183
|
+
getSelfArg(),
|
|
184
|
+
{ name: 'targets', type: 'Vec<Address>' },
|
|
185
|
+
{ name: 'functions', type: 'Vec<Symbol>' },
|
|
186
|
+
{ name: 'args', type: 'Vec<Vec<Val>>' },
|
|
187
|
+
{ name: 'description_hash', type: 'BytesN<32>' },
|
|
188
|
+
{ name: 'operator', type: 'Address' },
|
|
189
|
+
],
|
|
190
|
+
returns: 'BytesN<32>',
|
|
191
|
+
code: [
|
|
192
|
+
'let proposal_id = governor::hash_proposal(e, &targets, &functions, &args, &description_hash)',
|
|
193
|
+
'let proposer = governor::get_proposal_proposer(e, &proposal_id)',
|
|
194
|
+
'assert!(operator == proposer)',
|
|
195
|
+
'operator.require_auth()',
|
|
196
|
+
'governor::cancel(e, targets, functions, args, &description_hash)',
|
|
197
|
+
],
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const timelockFreeFunctions = defineFunctions({
|
|
202
|
+
timelock: {
|
|
203
|
+
args: [getSelfArg()],
|
|
204
|
+
returns: 'Address',
|
|
205
|
+
code: ['e.storage().instance().get(&GovernorTimelockKey::Timelock).expect("timelock not set")'],
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const timelockFunctions = defineFunctions({
|
|
210
|
+
proposals_need_queuing: {
|
|
211
|
+
args: [{ name: '_e', type: '&Env' }],
|
|
212
|
+
returns: 'bool',
|
|
213
|
+
code: ['true'],
|
|
214
|
+
},
|
|
215
|
+
queue: {
|
|
216
|
+
args: [
|
|
217
|
+
getSelfArg(),
|
|
218
|
+
{ name: 'targets', type: 'Vec<Address>' },
|
|
219
|
+
{ name: 'functions', type: 'Vec<Symbol>' },
|
|
220
|
+
{ name: 'args', type: 'Vec<Vec<Val>>' },
|
|
221
|
+
{ name: 'description_hash', type: 'BytesN<32>' },
|
|
222
|
+
{ name: 'eta', type: 'u32' },
|
|
223
|
+
{ name: '_operator', type: 'Address' },
|
|
224
|
+
],
|
|
225
|
+
returns: 'BytesN<32>',
|
|
226
|
+
code: [
|
|
227
|
+
'let proposal_id = governor::hash_proposal(e, &targets, &functions, &args, &description_hash)',
|
|
228
|
+
'let snapshot = governor::get_proposal_snapshot(e, &proposal_id)',
|
|
229
|
+
'let quorum = Self::quorum(e, snapshot)',
|
|
230
|
+
'let proposal_id = governor::queue(e, targets.clone(), functions.clone(), args.clone(), &description_hash, eta, quorum)',
|
|
231
|
+
'let timelock = Self::timelock(e)',
|
|
232
|
+
'let delay = eta.saturating_sub(e.ledger().sequence())',
|
|
233
|
+
'let execute_args: Vec<Val> = (targets, functions, args, description_hash.clone(), timelock.clone()).into_val(e)',
|
|
234
|
+
'let timelock_client = TimelockClient::new(e, &timelock)',
|
|
235
|
+
'let zero_predecessor = BytesN::from_array(e, &[0u8; 32])',
|
|
236
|
+
'let op_id = timelock_client.schedule(&e.current_contract_address(), &Symbol::new(e, "execute"), &execute_args, &zero_predecessor, &description_hash, &delay, &e.current_contract_address())',
|
|
237
|
+
'e.storage().persistent().set(&GovernorTimelockKey::OperationID(proposal_id.clone()), &op_id);',
|
|
238
|
+
'proposal_id',
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
execute: {
|
|
242
|
+
args: [
|
|
243
|
+
getSelfArg(),
|
|
244
|
+
{ name: 'targets', type: 'Vec<Address>' },
|
|
245
|
+
{ name: 'functions', type: 'Vec<Symbol>' },
|
|
246
|
+
{ name: 'args', type: 'Vec<Vec<Val>>' },
|
|
247
|
+
{ name: 'description_hash', type: 'BytesN<32>' },
|
|
248
|
+
{ name: 'executor', type: 'Address' },
|
|
249
|
+
],
|
|
250
|
+
returns: 'BytesN<32>',
|
|
251
|
+
code: [
|
|
252
|
+
'let timelock = Self::timelock(e)',
|
|
253
|
+
'assert!(executor == timelock)',
|
|
254
|
+
'executor.require_auth()',
|
|
255
|
+
'let proposal_id = governor::hash_proposal(e, &targets, &functions, &args, &description_hash)',
|
|
256
|
+
'let snapshot = governor::get_proposal_snapshot(e, &proposal_id)',
|
|
257
|
+
'let quorum = Self::quorum(e, snapshot)',
|
|
258
|
+
'governor::execute(e, targets, functions, args, &description_hash, Self::proposals_need_queuing(e), quorum)',
|
|
259
|
+
],
|
|
260
|
+
},
|
|
261
|
+
cancel: {
|
|
262
|
+
args: [
|
|
263
|
+
getSelfArg(),
|
|
264
|
+
{ name: 'targets', type: 'Vec<Address>' },
|
|
265
|
+
{ name: 'functions', type: 'Vec<Symbol>' },
|
|
266
|
+
{ name: 'args', type: 'Vec<Vec<Val>>' },
|
|
267
|
+
{ name: 'description_hash', type: 'BytesN<32>' },
|
|
268
|
+
{ name: 'operator', type: 'Address' },
|
|
269
|
+
],
|
|
270
|
+
returns: 'BytesN<32>',
|
|
271
|
+
code: [
|
|
272
|
+
'let proposal_id = governor::hash_proposal(e, &targets, &functions, &args, &description_hash)',
|
|
273
|
+
'let proposer = governor::get_proposal_proposer(e, &proposal_id)',
|
|
274
|
+
'assert!(operator == proposer)',
|
|
275
|
+
'operator.require_auth()',
|
|
276
|
+
'if let Some(op_id) = e.storage().persistent().get::<_, BytesN<32>>(&GovernorTimelockKey::OperationID(proposal_id)) {',
|
|
277
|
+
' let timelock = Self::timelock(e);',
|
|
278
|
+
' let timelock_client = TimelockClient::new(e, &timelock);',
|
|
279
|
+
' timelock_client.cancel(&op_id, &e.current_contract_address());',
|
|
280
|
+
'}',
|
|
281
|
+
'governor::cancel(e, targets, functions, args, &description_hash)',
|
|
282
|
+
],
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// Explicit-trait variants for every default method on the `Governor` trait.
|
|
287
|
+
// When `explicitImplementations` is enabled, these are emitted as overrides
|
|
288
|
+
// that mirror the upstream defaults — letting users see and customize each
|
|
289
|
+
// method directly. Order matches the trait definition for readability.
|
|
290
|
+
const governorTraitDefaultFunctions = defineFunctions({
|
|
291
|
+
name: {
|
|
292
|
+
args: [getSelfArg()],
|
|
293
|
+
returns: 'String',
|
|
294
|
+
code: ['governor::get_name(e)'],
|
|
295
|
+
},
|
|
296
|
+
version: {
|
|
297
|
+
args: [getSelfArg()],
|
|
298
|
+
returns: 'String',
|
|
299
|
+
code: ['governor::get_version(e)'],
|
|
300
|
+
},
|
|
301
|
+
voting_delay: {
|
|
302
|
+
args: [getSelfArg()],
|
|
303
|
+
returns: 'u32',
|
|
304
|
+
code: ['governor::get_voting_delay(e)'],
|
|
305
|
+
},
|
|
306
|
+
voting_period: {
|
|
307
|
+
args: [getSelfArg()],
|
|
308
|
+
returns: 'u32',
|
|
309
|
+
code: ['governor::get_voting_period(e)'],
|
|
310
|
+
},
|
|
311
|
+
proposal_threshold: {
|
|
312
|
+
args: [getSelfArg()],
|
|
313
|
+
returns: 'u128',
|
|
314
|
+
code: ['governor::get_proposal_threshold(e)'],
|
|
315
|
+
},
|
|
316
|
+
get_token_contract: {
|
|
317
|
+
args: [getSelfArg()],
|
|
318
|
+
returns: 'Address',
|
|
319
|
+
code: ['governor::get_token_contract(e)'],
|
|
320
|
+
},
|
|
321
|
+
counting_mode: {
|
|
322
|
+
args: [getSelfArg()],
|
|
323
|
+
returns: 'Symbol',
|
|
324
|
+
code: ['governor::counting_mode(e)'],
|
|
325
|
+
},
|
|
326
|
+
has_voted: {
|
|
327
|
+
args: [getSelfArg(), { name: 'proposal_id', type: 'BytesN<32>' }, { name: 'account', type: 'Address' }],
|
|
328
|
+
returns: 'bool',
|
|
329
|
+
code: ['governor::has_voted(e, &proposal_id, &account)'],
|
|
330
|
+
},
|
|
331
|
+
quorum: {
|
|
332
|
+
args: [getSelfArg(), { name: 'ledger', type: 'u32' }],
|
|
333
|
+
returns: 'u128',
|
|
334
|
+
code: ['governor::get_quorum(e, ledger)'],
|
|
335
|
+
},
|
|
336
|
+
proposal_state: {
|
|
337
|
+
args: [getSelfArg(), { name: 'proposal_id', type: 'BytesN<32>' }],
|
|
338
|
+
returns: 'ProposalState',
|
|
339
|
+
code: [
|
|
340
|
+
'let snapshot = governor::get_proposal_snapshot(e, &proposal_id)',
|
|
341
|
+
'let quorum = Self::quorum(e, snapshot)',
|
|
342
|
+
'governor::get_proposal_state(e, &proposal_id, quorum)',
|
|
343
|
+
],
|
|
344
|
+
},
|
|
345
|
+
proposal_snapshot: {
|
|
346
|
+
args: [getSelfArg(), { name: 'proposal_id', type: 'BytesN<32>' }],
|
|
347
|
+
returns: 'u32',
|
|
348
|
+
code: ['governor::get_proposal_snapshot(e, &proposal_id)'],
|
|
349
|
+
},
|
|
350
|
+
proposal_deadline: {
|
|
351
|
+
args: [getSelfArg(), { name: 'proposal_id', type: 'BytesN<32>' }],
|
|
352
|
+
returns: 'u32',
|
|
353
|
+
code: ['governor::get_proposal_deadline(e, &proposal_id)'],
|
|
354
|
+
},
|
|
355
|
+
proposal_proposer: {
|
|
356
|
+
args: [getSelfArg(), { name: 'proposal_id', type: 'BytesN<32>' }],
|
|
357
|
+
returns: 'Address',
|
|
358
|
+
code: ['governor::get_proposal_proposer(e, &proposal_id)'],
|
|
359
|
+
},
|
|
360
|
+
get_proposal_id: {
|
|
361
|
+
args: [
|
|
362
|
+
getSelfArg(),
|
|
363
|
+
{ name: 'targets', type: 'Vec<Address>' },
|
|
364
|
+
{ name: 'functions', type: 'Vec<Symbol>' },
|
|
365
|
+
{ name: 'args', type: 'Vec<Vec<Val>>' },
|
|
366
|
+
{ name: 'description_hash', type: 'BytesN<32>' },
|
|
367
|
+
],
|
|
368
|
+
returns: 'BytesN<32>',
|
|
369
|
+
code: ['governor::hash_proposal(e, &targets, &functions, &args, &description_hash)'],
|
|
370
|
+
},
|
|
371
|
+
propose: {
|
|
372
|
+
args: [
|
|
373
|
+
getSelfArg(),
|
|
374
|
+
{ name: 'targets', type: 'Vec<Address>' },
|
|
375
|
+
{ name: 'functions', type: 'Vec<Symbol>' },
|
|
376
|
+
{ name: 'args', type: 'Vec<Vec<Val>>' },
|
|
377
|
+
{ name: 'description', type: 'String' },
|
|
378
|
+
{ name: 'proposer', type: 'Address' },
|
|
379
|
+
],
|
|
380
|
+
returns: 'BytesN<32>',
|
|
381
|
+
code: ['proposer.require_auth()', 'governor::propose(e, targets, functions, args, description, &proposer)'],
|
|
382
|
+
},
|
|
383
|
+
cast_vote: {
|
|
384
|
+
args: [
|
|
385
|
+
getSelfArg(),
|
|
386
|
+
{ name: 'proposal_id', type: 'BytesN<32>' },
|
|
387
|
+
{ name: 'vote_type', type: 'u32' },
|
|
388
|
+
{ name: 'reason', type: 'String' },
|
|
389
|
+
{ name: 'voter', type: 'Address' },
|
|
390
|
+
],
|
|
391
|
+
returns: 'u128',
|
|
392
|
+
code: [
|
|
393
|
+
'voter.require_auth()',
|
|
394
|
+
'let snapshot = governor::get_proposal_snapshot(e, &proposal_id)',
|
|
395
|
+
'let quorum = Self::quorum(e, snapshot)',
|
|
396
|
+
'governor::cast_vote(e, &proposal_id, vote_type, &reason, &voter, quorum)',
|
|
397
|
+
],
|
|
398
|
+
},
|
|
399
|
+
queue: {
|
|
400
|
+
args: [
|
|
401
|
+
getSelfArg(),
|
|
402
|
+
{ name: 'targets', type: 'Vec<Address>' },
|
|
403
|
+
{ name: 'functions', type: 'Vec<Symbol>' },
|
|
404
|
+
{ name: 'args', type: 'Vec<Vec<Val>>' },
|
|
405
|
+
{ name: 'description_hash', type: 'BytesN<32>' },
|
|
406
|
+
{ name: 'eta', type: 'u32' },
|
|
407
|
+
{ name: '_operator', type: 'Address' },
|
|
408
|
+
],
|
|
409
|
+
returns: 'BytesN<32>',
|
|
410
|
+
code: [
|
|
411
|
+
'if !Self::proposals_need_queuing(e) {',
|
|
412
|
+
' soroban_sdk::panic_with_error!(e, governor::GovernorError::QueueNotEnabled);',
|
|
413
|
+
'}',
|
|
414
|
+
'let proposal_id = governor::hash_proposal(e, &targets, &functions, &args, &description_hash)',
|
|
415
|
+
'let snapshot = governor::get_proposal_snapshot(e, &proposal_id)',
|
|
416
|
+
'let quorum = Self::quorum(e, snapshot)',
|
|
417
|
+
'governor::queue(e, targets, functions, args, &description_hash, eta, quorum)',
|
|
418
|
+
],
|
|
419
|
+
},
|
|
420
|
+
proposals_need_queuing: {
|
|
421
|
+
args: [{ name: '_e', type: '&Env' }],
|
|
422
|
+
returns: 'bool',
|
|
423
|
+
code: ['false'],
|
|
424
|
+
},
|
|
425
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -22,9 +22,11 @@ export { sanitizeKind } from './kind';
|
|
|
22
22
|
export { contractsVersion, contractsVersionTag, compatibleContractsSemver } from './utils/version';
|
|
23
23
|
|
|
24
24
|
export { fungible } from './api';
|
|
25
|
+
export { governor } from './api';
|
|
25
26
|
export { nonFungible } from './api';
|
|
26
27
|
export { stablecoin } from './api';
|
|
27
28
|
|
|
28
29
|
export type { FungibleOptions } from './fungible';
|
|
30
|
+
export type { GovernorOptions } from './governor';
|
|
29
31
|
export type { NonFungibleOptions } from './non-fungible';
|
|
30
32
|
export type { StablecoinOptions } from './stablecoin';
|
package/src/kind.ts
CHANGED
package/src/non-fungible.ts
CHANGED
|
@@ -18,6 +18,7 @@ export const defaults: Required<NonFungibleOptions> = {
|
|
|
18
18
|
symbol: 'MTK',
|
|
19
19
|
tokenUri: 'https://www.mytoken.com',
|
|
20
20
|
burnable: false,
|
|
21
|
+
votes: false,
|
|
21
22
|
enumerable: false,
|
|
22
23
|
consecutive: false,
|
|
23
24
|
pausable: false,
|
|
@@ -38,6 +39,7 @@ export interface NonFungibleOptions extends CommonContractOptions {
|
|
|
38
39
|
symbol: string;
|
|
39
40
|
tokenUri?: string;
|
|
40
41
|
burnable?: boolean;
|
|
42
|
+
votes?: boolean;
|
|
41
43
|
enumerable?: boolean;
|
|
42
44
|
consecutive?: boolean;
|
|
43
45
|
pausable?: boolean;
|
|
@@ -52,6 +54,7 @@ function withDefaults(opts: NonFungibleOptions): Required<NonFungibleOptions> {
|
|
|
52
54
|
...withCommonContractDefaults(opts),
|
|
53
55
|
tokenUri: opts.tokenUri ?? defaults.tokenUri,
|
|
54
56
|
burnable: opts.burnable ?? defaults.burnable,
|
|
57
|
+
votes: opts.votes ?? defaults.votes,
|
|
55
58
|
consecutive: opts.consecutive ?? defaults.consecutive,
|
|
56
59
|
enumerable: opts.enumerable ?? defaults.enumerable,
|
|
57
60
|
pausable: opts.pausable ?? defaults.pausable,
|
|
@@ -87,6 +90,16 @@ export function buildNonFungible(opts: NonFungibleOptions): Contract {
|
|
|
87
90
|
errors.sequential = 'Sequential minting cannot be used with Consecutive extension';
|
|
88
91
|
}
|
|
89
92
|
|
|
93
|
+
if (allOpts.votes && allOpts.enumerable) {
|
|
94
|
+
errors.votes = 'Votes extension cannot be used with Enumerable extension';
|
|
95
|
+
errors.enumerable = 'Enumerable extension cannot be used with Votes extension';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (allOpts.votes && allOpts.consecutive) {
|
|
99
|
+
errors.votes = 'Votes extension cannot be used with Consecutive extension';
|
|
100
|
+
errors.consecutive = 'Consecutive extension cannot be used with Votes extension';
|
|
101
|
+
}
|
|
102
|
+
|
|
90
103
|
if (Object.keys(errors).length > 0) {
|
|
91
104
|
throw new OptionsError(errors);
|
|
92
105
|
}
|
|
@@ -96,6 +109,7 @@ export function buildNonFungible(opts: NonFungibleOptions): Contract {
|
|
|
96
109
|
toByteArray(allOpts.name),
|
|
97
110
|
toByteArray(allOpts.symbol),
|
|
98
111
|
toByteArray(allOpts.tokenUri),
|
|
112
|
+
allOpts.votes,
|
|
99
113
|
allOpts.pausable,
|
|
100
114
|
allOpts.explicitImplementations,
|
|
101
115
|
);
|
|
@@ -124,6 +138,7 @@ export function buildNonFungible(opts: NonFungibleOptions): Contract {
|
|
|
124
138
|
addMintable(
|
|
125
139
|
c,
|
|
126
140
|
allOpts.enumerable,
|
|
141
|
+
allOpts.votes,
|
|
127
142
|
allOpts.pausable,
|
|
128
143
|
allOpts.sequential,
|
|
129
144
|
allOpts.access,
|
|
@@ -142,6 +157,7 @@ function addBase(
|
|
|
142
157
|
name: string,
|
|
143
158
|
symbol: string,
|
|
144
159
|
tokenUri: string,
|
|
160
|
+
votes: boolean,
|
|
145
161
|
pausable: boolean,
|
|
146
162
|
explicitImplementations: boolean,
|
|
147
163
|
) {
|
|
@@ -154,19 +170,22 @@ function addBase(
|
|
|
154
170
|
// Set token functions
|
|
155
171
|
c.addUseClause('stellar_tokens::non_fungible', 'Base');
|
|
156
172
|
c.addUseClause('stellar_tokens::non_fungible', 'NonFungibleToken');
|
|
173
|
+
if (votes) {
|
|
174
|
+
c.addUseClause('stellar_governance::votes', 'Votes');
|
|
175
|
+
c.addUseClause('stellar_tokens::non_fungible', 'votes::NonFungibleVotes');
|
|
176
|
+
}
|
|
157
177
|
if (explicitImplementations) c.addUseClause('stellar_tokens::non_fungible', 'ContractOverrides');
|
|
158
|
-
else c.addUseClause('stellar_macros', 'default_impl');
|
|
159
178
|
c.addUseClause('soroban_sdk', 'contract');
|
|
160
179
|
c.addUseClause('soroban_sdk', 'contractimpl');
|
|
161
180
|
c.addUseClause('soroban_sdk', 'String');
|
|
162
181
|
c.addUseClause('soroban_sdk', 'Env');
|
|
163
|
-
|
|
182
|
+
c.addUseClause('soroban_sdk', 'Address');
|
|
164
183
|
|
|
165
184
|
const nonFungibleTokenTrait = {
|
|
166
185
|
traitName: 'NonFungibleToken',
|
|
167
186
|
structName: c.name,
|
|
168
|
-
tags: explicitImplementations ? ['contractimpl'] : ['
|
|
169
|
-
assocType:
|
|
187
|
+
tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
|
|
188
|
+
assocType: `type ContractType = ${votes ? 'NonFungibleVotes' : 'Base'};`,
|
|
170
189
|
};
|
|
171
190
|
|
|
172
191
|
c.addTraitImplBlock(nonFungibleTokenTrait);
|
|
@@ -182,6 +201,15 @@ function addBase(
|
|
|
182
201
|
c.addFunctionTag(baseFunctions.transfer_from, 'when_not_paused', nonFungibleTokenTrait);
|
|
183
202
|
c.addTraitFunction(nonFungibleTokenTrait, baseFunctions.transfer_from);
|
|
184
203
|
}
|
|
204
|
+
|
|
205
|
+
if (votes) {
|
|
206
|
+
c.addTraitImplBlock({
|
|
207
|
+
traitName: 'Votes',
|
|
208
|
+
structName: c.name,
|
|
209
|
+
tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
|
|
210
|
+
section: 'Extensions',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
185
213
|
}
|
|
186
214
|
|
|
187
215
|
function addBurnable(c: ContractBuilder, pausable: boolean, explicitImplementations: boolean) {
|
|
@@ -190,7 +218,7 @@ function addBurnable(c: ContractBuilder, pausable: boolean, explicitImplementati
|
|
|
190
218
|
const nonFungibleBurnableTrait = {
|
|
191
219
|
traitName: 'NonFungibleBurnable',
|
|
192
220
|
structName: c.name,
|
|
193
|
-
tags: ['contractimpl'],
|
|
221
|
+
tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
|
|
194
222
|
section: 'Extensions',
|
|
195
223
|
};
|
|
196
224
|
|
|
@@ -205,21 +233,17 @@ function addBurnable(c: ContractBuilder, pausable: boolean, explicitImplementati
|
|
|
205
233
|
} else if (explicitImplementations)
|
|
206
234
|
c.addTraitForEachFunctions(nonFungibleBurnableTrait, nonFungibleBurnableFunctions);
|
|
207
235
|
else {
|
|
208
|
-
// prepend '#[default_impl]'
|
|
209
|
-
nonFungibleBurnableTrait.tags.unshift('default_impl');
|
|
210
236
|
c.addTraitImplBlock(nonFungibleBurnableTrait);
|
|
211
237
|
}
|
|
212
238
|
}
|
|
213
239
|
|
|
214
240
|
function addEnumerable(c: ContractBuilder, explicitImplementations: boolean) {
|
|
215
241
|
c.addUseClause('stellar_tokens::non_fungible', 'enumerable::{NonFungibleEnumerable, Enumerable}');
|
|
216
|
-
if (explicitImplementations) c.addUseClause('soroban_sdk', 'Address');
|
|
217
|
-
else c.addUseClause('stellar_macros', 'default_impl');
|
|
218
242
|
|
|
219
243
|
const nonFungibleEnumerableTrait = {
|
|
220
244
|
traitName: 'NonFungibleEnumerable',
|
|
221
245
|
structName: c.name,
|
|
222
|
-
tags: explicitImplementations ? ['contractimpl'] : ['
|
|
246
|
+
tags: explicitImplementations ? ['contractimpl'] : ['contractimpl(contracttrait)'],
|
|
223
247
|
section: 'Extensions',
|
|
224
248
|
};
|
|
225
249
|
if (explicitImplementations)
|
|
@@ -231,7 +255,6 @@ function addEnumerable(c: ContractBuilder, explicitImplementations: boolean) {
|
|
|
231
255
|
|
|
232
256
|
function addConsecutive(c: ContractBuilder, pausable: boolean, access: Access, explicitImplementations: boolean) {
|
|
233
257
|
c.addUseClause('stellar_tokens::non_fungible', 'consecutive::{NonFungibleConsecutive, Consecutive}');
|
|
234
|
-
c.addUseClause('soroban_sdk', 'Address');
|
|
235
258
|
|
|
236
259
|
const effectiveAccess = access === false ? DEFAULT_ACCESS_CONTROL : access;
|
|
237
260
|
const nonFungibleConsecutiveTrait = {
|
|
@@ -269,12 +292,12 @@ function addConsecutive(c: ContractBuilder, pausable: boolean, access: Access, e
|
|
|
269
292
|
function addMintable(
|
|
270
293
|
c: ContractBuilder,
|
|
271
294
|
enumerable: boolean,
|
|
295
|
+
votes: boolean,
|
|
272
296
|
pausable: boolean,
|
|
273
297
|
sequential: boolean,
|
|
274
298
|
access: Access,
|
|
275
299
|
explicitImplementations: boolean,
|
|
276
300
|
) {
|
|
277
|
-
c.addUseClause('soroban_sdk', 'Address');
|
|
278
301
|
const accessProps = { useMacro: true, role: 'minter', caller: 'caller' };
|
|
279
302
|
const effectiveAccess = access === false ? DEFAULT_ACCESS_CONTROL : access;
|
|
280
303
|
|
|
@@ -295,9 +318,22 @@ function addMintable(
|
|
|
295
318
|
} else {
|
|
296
319
|
if (sequential) {
|
|
297
320
|
mintFn =
|
|
298
|
-
effectiveAccess === 'ownable'
|
|
321
|
+
effectiveAccess === 'ownable'
|
|
322
|
+
? votes
|
|
323
|
+
? votesMintFunctions.sequential_mint
|
|
324
|
+
: baseFunctions.sequential_mint
|
|
325
|
+
: votes
|
|
326
|
+
? votesMintFunctions.sequential_mint_with_caller
|
|
327
|
+
: baseFunctions.sequential_mint_with_caller;
|
|
299
328
|
} else {
|
|
300
|
-
mintFn =
|
|
329
|
+
mintFn =
|
|
330
|
+
effectiveAccess === 'ownable'
|
|
331
|
+
? votes
|
|
332
|
+
? votesMintFunctions.mint
|
|
333
|
+
: baseFunctions.mint
|
|
334
|
+
: votes
|
|
335
|
+
? votesMintFunctions.mint_with_caller
|
|
336
|
+
: baseFunctions.mint_with_caller;
|
|
301
337
|
}
|
|
302
338
|
}
|
|
303
339
|
|
|
@@ -412,6 +448,33 @@ const baseFunctions = defineFunctions({
|
|
|
412
448
|
},
|
|
413
449
|
});
|
|
414
450
|
|
|
451
|
+
const votesMintFunctions = defineFunctions({
|
|
452
|
+
mint: {
|
|
453
|
+
args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'token_id', type: 'u32' }],
|
|
454
|
+
code: ['NonFungibleVotes::mint(e, &to, token_id);'],
|
|
455
|
+
},
|
|
456
|
+
mint_with_caller: {
|
|
457
|
+
name: 'mint',
|
|
458
|
+
args: [
|
|
459
|
+
getSelfArg(),
|
|
460
|
+
{ name: 'to', type: 'Address' },
|
|
461
|
+
{ name: 'token_id', type: 'u32' },
|
|
462
|
+
{ name: 'caller', type: 'Address' },
|
|
463
|
+
],
|
|
464
|
+
code: ['NonFungibleVotes::mint(e, &to, token_id);'],
|
|
465
|
+
},
|
|
466
|
+
sequential_mint: {
|
|
467
|
+
name: 'mint',
|
|
468
|
+
args: [getSelfArg(), { name: 'to', type: 'Address' }],
|
|
469
|
+
code: ['NonFungibleVotes::sequential_mint(e, &to);'],
|
|
470
|
+
},
|
|
471
|
+
sequential_mint_with_caller: {
|
|
472
|
+
name: 'mint',
|
|
473
|
+
args: [getSelfArg(), { name: 'to', type: 'Address' }, { name: 'caller', type: 'Address' }],
|
|
474
|
+
code: ['NonFungibleVotes::sequential_mint(e, &to);'],
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
|
|
415
478
|
const nonFungibleTokenTraitFunctions = pickKeys(baseFunctions, [
|
|
416
479
|
'balance',
|
|
417
480
|
'owner_of',
|
package/src/print.ts
CHANGED
|
@@ -4,7 +4,6 @@ import type { Lines } from './utils/format-lines';
|
|
|
4
4
|
import { formatLines, spaceBetween } from './utils/format-lines';
|
|
5
5
|
import { getSelfArg } from './common-options';
|
|
6
6
|
import { compatibleContractsSemver } from './utils/version';
|
|
7
|
-
import { toByteArray } from './utils/convert-strings';
|
|
8
7
|
|
|
9
8
|
const DEFAULT_SECTION = '1. with no section';
|
|
10
9
|
const STANDALONE_IMPORTS_GROUP = 'Standalone Imports';
|
|
@@ -31,6 +30,7 @@ export function printContract(contract: Contract): string {
|
|
|
31
30
|
printUseClauses(contract),
|
|
32
31
|
printMetadata(contract),
|
|
33
32
|
printVariables(contract),
|
|
33
|
+
printTopLevelDeclarations(contract),
|
|
34
34
|
printContractStruct(contract),
|
|
35
35
|
printContractErrors(contract),
|
|
36
36
|
printContractFunctions(contract),
|
|
@@ -364,6 +364,11 @@ function printArgument(arg: Argument): string {
|
|
|
364
364
|
}
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
+
function printTopLevelDeclarations(contract: Contract): Lines[] {
|
|
368
|
+
if (contract.topLevelDeclarations.length === 0) return [];
|
|
369
|
+
return spaceBetween(...contract.topLevelDeclarations);
|
|
370
|
+
}
|
|
371
|
+
|
|
367
372
|
function printDocumentations(documentations: string[]): string[] {
|
|
368
373
|
return documentations.map(documentation => `//! ${documentation}`);
|
|
369
374
|
}
|