@@ -29,6 +29,7 @@ export const defaults: Required<ERC721Options> = {
2929 wrapper : false ,
3030 uriStorage : false ,
3131 votes : false ,
32+ consecutive : false ,
3233 royaltyInfo : royaltyInfoDefaults ,
3334 appName : '' , // Defaults to empty string, but user must provide a non-empty value if votes are enabled
3435 appVersion : 'v1' ,
@@ -53,6 +54,7 @@ export interface ERC721Options extends CommonContractOptions {
5354 wrapper ?: boolean ;
5455 uriStorage ?: boolean ;
5556 votes ?: boolean ;
57+ consecutive ?: boolean ;
5658 royaltyInfo ?: RoyaltyInfoOptions ;
5759 appName ?: string ;
5860 appVersion ?: string ;
@@ -71,6 +73,7 @@ function withDefaults(opts: ERC721Options): Required<ERC721Options> {
7173 uriStorage : opts . uriStorage ?? defaults . uriStorage ,
7274 royaltyInfo : opts . royaltyInfo ?? defaults . royaltyInfo ,
7375 votes : opts . votes ?? defaults . votes ,
76+ consecutive : opts . consecutive ?? defaults . consecutive ,
7477 appName : opts . appName ?? defaults . appName ,
7578 appVersion : opts . appVersion ?? defaults . appVersion ,
7679 } ;
@@ -89,11 +92,20 @@ export function isAccessControlRequired(opts: Partial<ERC721Options>): boolean {
8992export function buildERC721 ( opts : ERC721Options ) : Contract {
9093 const allOpts = withDefaults ( opts ) ;
9194
95+ if ( allOpts . consecutive && allOpts . enumerable ) {
96+ throw new OptionsError ( {
97+ consecutive :
98+ 'Consecutive cannot be combined with Enumerable: batch mints bypass the update hook that Enumerable relies on to track ownership.' ,
99+ } ) ;
100+ }
101+
92102 const c = new ContractBuilder ( allOpts . name , allOpts . macros ) ;
93103
94104 addBase ( c , toByteArray ( allOpts . name ) , toByteArray ( allOpts . symbol ) , toByteArray ( allOpts . baseUri ) ) ;
95105 addERC721Mixin ( c ) ;
96- c . addUseClause ( 'openzeppelin_token::erc721' , 'ERC721OwnerOfDefaultImpl' ) ;
106+ if ( ! allOpts . consecutive ) {
107+ c . addUseClause ( 'openzeppelin_token::erc721' , 'ERC721OwnerOfDefaultImpl' ) ;
108+ }
97109
98110 if ( allOpts . pausable ) {
99111 addPausable ( c , allOpts . access ) ;
@@ -111,6 +123,10 @@ export function buildERC721(opts: ERC721Options): Contract {
111123 addEnumerable ( c ) ;
112124 }
113125
126+ if ( allOpts . consecutive ) {
127+ addConsecutive ( c ) ;
128+ }
129+
114130 if ( allOpts . wrapper ) {
115131 addWrapper ( c ) ;
116132 }
@@ -151,8 +167,8 @@ function addERC721Mixin(c: ContractBuilder) {
151167/* Hooks */
152168
153169function addHooks ( c : ContractBuilder , opts : Required < ERC721Options > ) {
154- const hasBeforeUpdateHook = opts . pausable || opts . enumerable || opts . votes ;
155- const hasAfterUpdateHook = opts . uriStorage ;
170+ const hasBeforeUpdateHook = opts . pausable || opts . enumerable || opts . votes || opts . consecutive ;
171+ const hasAfterUpdateHook = opts . uriStorage || opts . consecutive ;
156172 const hasCustomHooks = hasBeforeUpdateHook || hasAfterUpdateHook ;
157173 if ( hasCustomHooks ) {
158174 const ERC721HooksTrait : BaseImplementedTrait = {
@@ -176,10 +192,10 @@ function addHooks(c: ContractBuilder, opts: Required<ERC721Options>) {
176192}
177193
178194function addBeforeUpdateHook ( c : ContractBuilder , trait : BaseImplementedTrait , opts : Required < ERC721Options > ) {
179- if ( ! opts . pausable && ! opts . enumerable && ! opts . votes ) {
195+ if ( ! opts . pausable && ! opts . enumerable && ! opts . votes && ! opts . consecutive ) {
180196 return ;
181197 }
182- const requiresMutState = opts . enumerable || opts . votes ;
198+ const requiresMutState = opts . enumerable || opts . votes || opts . consecutive ;
183199 const initStateLine = requiresMutState
184200 ? 'let mut contract_state = self.get_contract_mut()'
185201 : 'let contract_state = self.get_contract()' ;
@@ -210,6 +226,9 @@ function addBeforeUpdateHook(c: ContractBuilder, trait: BaseImplementedTrait, op
210226 beforeUpdateCode . push ( 'let previous_owner = self._owner_of(token_id);' ) ;
211227 beforeUpdateCode . push ( 'contract_state.votes.transfer_voting_units(previous_owner, to, 1);' ) ;
212228 }
229+ if ( opts . consecutive ) {
230+ beforeUpdateCode . push ( 'contract_state.erc721_consecutive.before_update(to, token_id, auth)' ) ;
231+ }
213232 c . addFunction ( trait , {
214233 name : 'before_update' ,
215234 args : [
@@ -226,14 +245,17 @@ function addBeforeUpdateHook(c: ContractBuilder, trait: BaseImplementedTrait, op
226245}
227246
228247function addAfterUpdateHook ( c : ContractBuilder , trait : BaseImplementedTrait , opts : Required < ERC721Options > ) {
229- if ( ! opts . uriStorage ) {
248+ if ( ! opts . uriStorage && ! opts . consecutive ) {
230249 return ;
231250 }
232251 const initStateLine = 'let mut contract_state = self.get_contract_mut()' ;
233252 const afterUpdateCode = [ initStateLine ] ;
234253 if ( opts . uriStorage ) {
235254 afterUpdateCode . push ( 'contract_state.erc721_uri_storage.after_update(to, token_id, auth)' ) ;
236255 }
256+ if ( opts . consecutive ) {
257+ afterUpdateCode . push ( 'contract_state.erc721_consecutive.after_update(to, token_id, auth)' ) ;
258+ }
237259 c . addFunction ( trait , {
238260 name : 'after_update' ,
239261 args : [
@@ -255,6 +277,11 @@ function addEnumerable(c: ContractBuilder) {
255277 c . addComponent ( components . ERC721EnumerableComponent , [ ] , true ) ;
256278}
257279
280+ function addConsecutive ( c : ContractBuilder ) {
281+ c . addUseClause ( 'openzeppelin_token::erc721::extensions::erc721_consecutive' , 'DefaultConfig' ) ;
282+ c . addComponent ( components . ERC721ConsecutiveComponent , [ ] , false ) ;
283+ }
284+
258285function addBurnable ( c : ContractBuilder ) {
259286 c . addUseClause ( 'core::num::traits' , 'Zero' ) ;
260287 c . addUseClause ( 'starknet' , 'get_caller_address' ) ;
@@ -370,6 +397,24 @@ const components = defineComponents({
370397 } ,
371398 ] ,
372399 } ,
400+ ERC721ConsecutiveComponent : {
401+ path : 'openzeppelin_token::erc721::extensions' ,
402+ substorage : {
403+ name : 'erc721_consecutive' ,
404+ type : 'ERC721ConsecutiveComponent::Storage' ,
405+ } ,
406+ event : {
407+ name : 'ERC721ConsecutiveEvent' ,
408+ type : 'ERC721ConsecutiveComponent::Event' ,
409+ } ,
410+ impls : [
411+ {
412+ name : 'ERC721ConsecutiveInternalImpl' ,
413+ embed : false ,
414+ value : 'ERC721ConsecutiveComponent::InternalImpl<ContractState>' ,
415+ } ,
416+ ] ,
417+ } ,
373418} ) ;
374419
375420const functions = defineFunctions ( {
0 commit comments