1
1
import { BigNumber } from '@ethersproject/bignumber'
2
2
import { ContractTransaction , PayableOverrides } from '@ethersproject/contracts'
3
+ import { SoundEditionV1_2__factory } from '@soundxyz/sound-protocol/typechain'
4
+
5
+ import { InvalidAttributonIdError , InvalidQuantityError , NotEligibleMint } from '../../errors'
6
+ import { MintOptions , MintSchedule , MintToOptions } from '../../types'
3
7
import {
4
- MerkleDropMinter__factory ,
5
- RangeEditionMinter__factory ,
6
- SoundEditionV1_2__factory ,
7
- } from '@soundxyz/sound-protocol/typechain'
8
-
9
- import { InvalidQuantityError , NotEligibleMint } from '../../errors'
10
- import { MintOptions , MintSchedule } from '../../types'
11
- import { MINT_FALLBACK_GAS_LIMIT , MINT_GAS_LIMIT_MULTIPLIER , NULL_ADDRESS } from '../../utils/constants'
12
- import { scaleAmount } from '../../utils/helpers'
8
+ MINT_FALLBACK_GAS_LIMIT ,
9
+ MINT_GAS_LIMIT_MULTIPLIER ,
10
+ minterFactoryMap ,
11
+ NULL_ADDRESS ,
12
+ } from '../../utils/constants'
13
+ import { exhaustiveGuard , scaleAmount } from '../../utils/helpers'
13
14
import { SoundClientInstance } from '../instance'
14
15
import { validateSoundEdition } from '../validation'
15
16
import { getMerkleProof } from './merkle'
16
17
import { isSchedulePaused } from './schedules'
18
+ import { interfaceIds } from '@soundxyz/sound-protocol/interfaceIds'
19
+ import { isBigNumberish } from '@ethersproject/bignumber/lib/bignumber'
17
20
18
21
export async function numberOfTokensOwned (
19
22
this : SoundClientInstance ,
@@ -69,9 +72,13 @@ export async function mint(
69
72
maxPriorityFeePerGas,
70
73
}
71
74
72
- switch ( mintSchedule . mintType ) {
73
- case 'RangeEdition' : {
74
- const rangeMinter = RangeEditionMinter__factory . connect ( mintSchedule . minterAddress , signer )
75
+ const interfaceId = mintSchedule . interfaceId
76
+
77
+ switch ( interfaceId ) {
78
+ case interfaceIds . IRangeEditionMinter :
79
+ case interfaceIds . IRangeEditionMinterV2 : {
80
+ const rangeMinter = minterFactoryMap [ interfaceId ] . connect ( mintSchedule . minterAddress , signer )
81
+
75
82
const mintArgs = [ mintSchedule . editionAddress , mintSchedule . mintId , quantity , affiliate ] as const
76
83
77
84
if ( txnOverrides . gasLimit ) {
@@ -91,8 +98,9 @@ export async function mint(
91
98
return rangeMinter . mint ( ...mintArgs , txnOverrides )
92
99
}
93
100
94
- case 'MerkleDrop' : {
95
- const merkleDropMinter = MerkleDropMinter__factory . connect ( mintSchedule . minterAddress , signer )
101
+ case interfaceIds . IMerkleDropMinter :
102
+ case interfaceIds . IMerkleDropMinterV2 : {
103
+ const merkleDropMinter = minterFactoryMap [ interfaceId ] . connect ( mintSchedule . minterAddress , signer )
96
104
97
105
const { merkleRootHash : merkleRoot } = await merkleDropMinter . mintInfo (
98
106
mintSchedule . editionAddress ,
@@ -131,8 +139,145 @@ export async function mint(
131
139
return merkleDropMinter . mint ( ...mintArgs , txnOverrides )
132
140
}
133
141
134
- default :
135
- throw new Error ( 'Unimplemented' )
142
+ default : {
143
+ exhaustiveGuard ( interfaceId )
144
+ }
145
+ }
146
+ }
147
+
148
+ export async function mintTo (
149
+ this : SoundClientInstance ,
150
+ {
151
+ affiliate = NULL_ADDRESS ,
152
+ affiliateProof = [ ] ,
153
+ attributonId = 0 ,
154
+ gasLimit,
155
+ maxFeePerGas,
156
+ maxPriorityFeePerGas,
157
+ mintSchedule,
158
+ mintToAddress,
159
+ quantity,
160
+ } : MintToOptions ,
161
+ ) : Promise < ContractTransaction > {
162
+ if ( ! isBigNumberish ( attributonId ) ) {
163
+ throw new InvalidAttributonIdError ( {
164
+ attributonId,
165
+ } )
166
+ }
167
+
168
+ await validateSoundEdition . call ( this , { editionAddress : mintSchedule . editionAddress } )
169
+ if ( quantity <= 0 || Math . floor ( quantity ) !== quantity ) throw new InvalidQuantityError ( { quantity } )
170
+
171
+ const { signer, userAddress } = await this . expectSigner ( )
172
+
173
+ const toAddress = mintToAddress ?? userAddress
174
+
175
+ const eligibleMintQuantity = await eligibleQuantity . call ( this , {
176
+ mintSchedule,
177
+ userAddress,
178
+ } )
179
+ if ( eligibleMintQuantity < quantity ) {
180
+ throw new NotEligibleMint ( {
181
+ eligibleMintQuantity,
182
+ mintSchedule,
183
+ userAddress,
184
+ } )
185
+ }
186
+
187
+ const txnOverrides : PayableOverrides = {
188
+ value : 'price' in mintSchedule ? mintSchedule . price . mul ( quantity ) : BigNumber . from ( '0' ) ,
189
+ gasLimit,
190
+ maxFeePerGas,
191
+ maxPriorityFeePerGas,
192
+ }
193
+
194
+ const interfaceId = mintSchedule . interfaceId
195
+
196
+ switch ( interfaceId ) {
197
+ case interfaceIds . IRangeEditionMinterV2 : {
198
+ const rangeMinter = minterFactoryMap [ interfaceId ] . connect ( mintSchedule . minterAddress , signer )
199
+
200
+ const mintArgs = [
201
+ mintSchedule . editionAddress ,
202
+ mintSchedule . mintId ,
203
+ toAddress ,
204
+ quantity ,
205
+ affiliate ,
206
+ affiliateProof ,
207
+ attributonId ,
208
+ ] as const
209
+
210
+ if ( txnOverrides . gasLimit ) {
211
+ return rangeMinter . mintTo ( ...mintArgs , txnOverrides )
212
+ }
213
+
214
+ try {
215
+ // Add a buffer to the gas estimate to account for node provider estimate variance.
216
+ const gasEstimate = await rangeMinter . estimateGas . mintTo ( ...mintArgs , txnOverrides )
217
+
218
+ txnOverrides . gasLimit = scaleAmount ( { amount : gasEstimate , multiplier : MINT_GAS_LIMIT_MULTIPLIER } )
219
+ } catch ( err ) {
220
+ // If estimation fails, provide a hardcoded gas limit that is guaranteed to succeed.
221
+ txnOverrides . gasLimit = MINT_FALLBACK_GAS_LIMIT
222
+ }
223
+
224
+ return rangeMinter . mintTo ( ...mintArgs , txnOverrides )
225
+ }
226
+
227
+ case interfaceIds . IMerkleDropMinterV2 : {
228
+ const merkleDropMinter = minterFactoryMap [ interfaceId ] . connect ( mintSchedule . minterAddress , signer )
229
+
230
+ const { merkleRootHash : merkleRoot } = await merkleDropMinter . mintInfo (
231
+ mintSchedule . editionAddress ,
232
+ mintSchedule . mintId ,
233
+ )
234
+
235
+ const proof = await getMerkleProof . call ( this , {
236
+ merkleRoot,
237
+ userAddress,
238
+ } )
239
+
240
+ if ( ! proof ?. length ) {
241
+ throw new NotEligibleMint ( {
242
+ mintSchedule,
243
+ userAddress,
244
+ eligibleMintQuantity,
245
+ } )
246
+ }
247
+
248
+ const mintArgs = [
249
+ mintSchedule . editionAddress ,
250
+ mintSchedule . mintId ,
251
+ toAddress ,
252
+ quantity ,
253
+ // TODO, allow overriding allowlisted for delegatecash
254
+ toAddress ,
255
+ proof ,
256
+ affiliate ,
257
+ affiliateProof ,
258
+ attributonId ,
259
+ ] as const
260
+
261
+ if ( txnOverrides . gasLimit ) {
262
+ return merkleDropMinter . mintTo ( ...mintArgs , txnOverrides )
263
+ }
264
+
265
+ try {
266
+ // Add a buffer to the gas estimate to account for node provider estimate variance.
267
+ const gasEstimate = await merkleDropMinter . estimateGas . mintTo ( ...mintArgs , txnOverrides )
268
+
269
+ txnOverrides . gasLimit = scaleAmount ( { amount : gasEstimate , multiplier : MINT_GAS_LIMIT_MULTIPLIER } )
270
+ } catch ( err ) {
271
+ // If estimation fails, provide a hardcoded gas limit that is guaranteed to succeed.
272
+ txnOverrides . gasLimit = MINT_FALLBACK_GAS_LIMIT
273
+ }
274
+
275
+ return merkleDropMinter . mintTo ( ...mintArgs , txnOverrides )
276
+ }
277
+
278
+ default : {
279
+ exhaustiveGuard ( interfaceId )
280
+ }
136
281
}
137
282
}
138
283
0 commit comments