-
Notifications
You must be signed in to change notification settings - Fork 105
/
web3.js
182 lines (163 loc) · 7.03 KB
/
web3.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import { getWalletAddressOrConnect, web3 } from "../wallet.js";
import { formatValue} from "../utils.js";
import { NFTContract } from "../contract.js"
import { buildTx } from "../tx";
import { readOnlyWeb3 } from "../web3";
const findMethodByName = (methodName) =>
Object.keys(NFTContract.methods)
.find(key => key.toLowerCase() === methodName.toLowerCase())
const getMethodWithCustomName = (methodName) => {
const method = window.DEFAULTS?.contractMethods ? window.DEFAULTS?.contractMethods[methodName] : undefined
if (method) {
console.log(`Using custom ${methodName} method name: `, method)
if (NFTContract.methods[method]) {
return NFTContract.methods[method]
} else {
alert(`Custom ${methodName} name isn't present in the ABI, using default name`)
console.log(`Custom ${methodName} name isn't present in the ABI, using default name`)
}
}
return undefined
}
const getMintTx = ({ numberOfTokens }) => {
const customMintMethod = getMethodWithCustomName('mint')
if (customMintMethod)
return customMintMethod(numberOfTokens)
console.log("Using hardcoded mint method detection")
const methodNameVariants = ['mint', 'publicMint', 'mintNFTs', 'mintPublic', 'mintSale']
const name = methodNameVariants.find(n => findMethodByName(n) !== undefined)
if (!name) {
alert("Buildship widget doesn't know how to mint from your contract. Contact https://buildship.xyz in Discord to resolve this.")
return undefined
}
return NFTContract.methods[findMethodByName(name)](numberOfTokens);
}
const getMintPriceConstant = () => {
// for contracts without exported price variable or method
const defaultPrice = window.DEFAULTS?.publicMint?.price
if (defaultPrice) {
const priceNumber = typeof defaultPrice === "string" ? Number(defaultPrice) : defaultPrice
if (isNaN(priceNumber)) {
alert("Wrong publicMintPrice format, should be a number in ETH (or native token)")
return undefined
}
console.warn("Using DEFAULTS.publicMint.price as price not found in the smart-contract")
return (priceNumber * 1e18).toString()
}
return undefined
}
export const getMintPrice = async () => {
const customMintPriceMethod = getMethodWithCustomName('price')
if (customMintPriceMethod) {
return customMintPriceMethod().call()
}
const mintPriceConstant = getMintPriceConstant()
if (mintPriceConstant !== undefined) {
console.log("Using constant mint price specified in DEFAULTS")
return mintPriceConstant
}
const matches = Object.keys(NFTContract.methods).filter(key =>
!key.includes("()") && (key.toLowerCase().includes('price') || key.toLowerCase().includes('cost'))
)
switch (matches.length) {
// Use auto-detection only when sure
// Otherwise this code might accidentally use presale price instead of public minting price
case 1:
console.log("Using price method auto-detection")
return NFTContract.methods[matches[0]]().call()
default:
console.log("Using hardcoded price detection")
const methodNameVariants = ['price', 'cost', 'public_sale_price', 'getPrice', 'salePrice']
const name = methodNameVariants.find(n => findMethodByName(n) !== undefined)
if (!name) {
alert("Buildship widget doesn't know how to fetch price from your contract. Contact https://buildship.xyz in Discord to resolve this.")
return undefined
}
return NFTContract.methods[findMethodByName(name)]().call();
}
}
export const getMintedNumber = async () => {
if (!NFTContract)
return undefined
const customTotalSupplyMethod = getMethodWithCustomName('totalSupply')
if (customTotalSupplyMethod)
return await customTotalSupplyMethod().call()
if (NFTContract.methods.totalSupply)
return await NFTContract.methods.totalSupply().call()
// temporary solution, works only for buildship.xyz contracts
// totalSupply was removed to save gas when minting
// but number minted still accessible in the contract as a private variable
// TODO: remove this in NFTFactory v1.1
const minted = await readOnlyWeb3.eth.getStorageAt(
NFTContract._address,
'0x00000000000000000000000000000000000000000000000000000000000000fb'
)
return readOnlyWeb3.utils.hexToNumber(minted)
}
export const getMaxSupply = async () => {
if (!NFTContract)
return undefined
const customMaxSupplyMethod = getMethodWithCustomName('maxSupply')
if (customMaxSupplyMethod)
return await customMaxSupplyMethod().call()
if (NFTContract.methods.maxSupply)
return await NFTContract.methods.maxSupply().call()
if (NFTContract.methods.MAX_SUPPLY)
return await NFTContract.methods.MAX_SUPPLY().call()
alert("Widget doesn't know how to fetch maxSupply from your contract. Contact https://buildship.xyz to resolve this.")
return undefined
}
export const getDefaultMaxTokensPerMint = () => {
const defaultMaxPerMintConfig = window.DEFAULTS?.publicMint?.maxPerMint || window.MAX_PER_MINT
if (!defaultMaxPerMintConfig || isNaN(Number(defaultMaxPerMintConfig))) {
console.error("Can't read maxPerMint from your contract & config, using default value: 10")
return 10
}
return Number(defaultMaxPerMintConfig)
}
export const getMaxTokensPerMint = async () => {
const customMaxPerMintMethod = getMethodWithCustomName('maxPerMint')
if (customMaxPerMintMethod)
return await customMaxPerMintMethod().call().then(Number)
if (NFTContract?.methods?.maxPerMint) {
return Number(await NFTContract.methods.maxPerMint().call())
}
if (NFTContract?.methods?.maxMintAmount) {
return Number(await NFTContract.methods.maxMintAmount().call())
}
if (NFTContract?.methods?.MAX_TOKENS_PER_MINT) {
return Number(await NFTContract.methods.MAX_TOKENS_PER_MINT().call())
}
return getDefaultMaxTokensPerMint()
}
export const mint = async (nTokens) => {
const wallet = await getWalletAddressOrConnect(true);
if (!wallet) {
return { tx: undefined }
}
const numberOfTokens = nTokens ?? 1;
const mintPrice = await getMintPrice();
if (mintPrice === undefined)
return { tx: undefined }
const txParams = {
from: wallet,
value: formatValue(Number(mintPrice) * numberOfTokens),
}
const mintTx = await getMintTx({ numberOfTokens })
if (!mintTx) {
return { tx: undefined }
}
const txBuilder = await buildTx(
mintTx,
txParams,
// TODO: use different limits for ERC721A / ERC721
window.DEFAULTS?.publicMint?.defaultGasLimit ?? (100000 * numberOfTokens),
window.DEFAULTS?.publicMint?.gasLimitSlippage ?? 5000
)
if (!txBuilder) {
return { tx: undefined }
}
const tx = mintTx.send(txBuilder)
// https://github.com/ChainSafe/web3.js/issues/1547
return Promise.resolve({ tx })
}