@monygroupcorp/micro-web3 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/README.md +87 -0
- package/dist/micro-web3.cjs.js +10 -0
- package/dist/micro-web3.cjs.js.map +1 -0
- package/dist/micro-web3.esm.js +10 -0
- package/dist/micro-web3.esm.js.map +1 -0
- package/dist/micro-web3.umd.js +10 -0
- package/dist/micro-web3.umd.js.map +1 -0
- package/package.json +34 -0
- package/rollup.config.cjs +36 -0
- package/src/components/BondingCurve/BondingCurve.js +296 -0
- package/src/components/Display/BalanceDisplay.js +81 -0
- package/src/components/Display/PriceDisplay.js +214 -0
- package/src/components/Ipfs/IpfsImage.js +265 -0
- package/src/components/Modal/ApprovalModal.js +398 -0
- package/src/components/Swap/SwapButton.js +81 -0
- package/src/components/Swap/SwapInputs.js +137 -0
- package/src/components/Swap/SwapInterface.js +972 -0
- package/src/components/Swap/TransactionOptions.js +238 -0
- package/src/components/Util/MessagePopup.js +159 -0
- package/src/components/Wallet/WalletModal.js +69 -0
- package/src/components/Wallet/WalletSplash.js +567 -0
- package/src/index.js +43 -0
- package/src/services/BlockchainService.js +1576 -0
- package/src/services/ContractCache.js +348 -0
- package/src/services/IpfsService.js +249 -0
- package/src/services/PriceService.js +191 -0
- package/src/services/WalletService.js +541 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { Component } from '@monygroupcorp/microact';
|
|
2
|
+
|
|
3
|
+
export class TransactionOptions extends Component {
|
|
4
|
+
constructor(props) {
|
|
5
|
+
super(props);
|
|
6
|
+
this.eventBus = props.eventBus;
|
|
7
|
+
|
|
8
|
+
this.state = {
|
|
9
|
+
nftMintingEnabled: props.nftMintingEnabled || false,
|
|
10
|
+
message: props.message || '',
|
|
11
|
+
isValid: props.isValid !== undefined ? props.isValid : true,
|
|
12
|
+
nftBalance: props.nftBalance || 0,
|
|
13
|
+
swapDirection: props.swapDirection || 'buy',
|
|
14
|
+
lastValidationState: true,
|
|
15
|
+
isPhase2: props.isPhase2 === null ? null : props.isPhase2
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
this.handleMessageInput = this.handleMessageInput.bind(this);
|
|
19
|
+
this.handleNFTToggle = this.handleNFTToggle.bind(this);
|
|
20
|
+
this.handleStoreUpdate = this.handleStoreUpdate.bind(this);
|
|
21
|
+
|
|
22
|
+
this.updateTimer = null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onMount() {
|
|
26
|
+
this.contractDataUnsubscribe = this.eventBus.on('contractData:updated', (data) => {
|
|
27
|
+
this.updateProps({ isPhase2: data.liquidityPool && data.liquidityPool !== '0x0000000000000000000000000000000000000000' });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
this.addEventListeners();
|
|
31
|
+
this.updateElements();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
onUnmount() {
|
|
35
|
+
if (this.contractDataUnsubscribe) this.contractDataUnsubscribe();
|
|
36
|
+
this.removeEventListeners();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
updateProps(newProps) {
|
|
40
|
+
const newState = { ...this.state, ...newProps };
|
|
41
|
+
const shouldUpdate = this.state.isPhase2 !== newState.isPhase2 ||
|
|
42
|
+
this.state.swapDirection !== newState.swapDirection ||
|
|
43
|
+
this.state.nftBalance !== newState.nftBalance;
|
|
44
|
+
|
|
45
|
+
this.setState(newState);
|
|
46
|
+
|
|
47
|
+
if (shouldUpdate) {
|
|
48
|
+
this.validateTransaction();
|
|
49
|
+
this.update(); // Re-render if phase or other critical props change
|
|
50
|
+
} else {
|
|
51
|
+
this.updateElements(); // Just update dynamic parts
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
addEventListeners() {
|
|
56
|
+
const messageInput = this.element.querySelector('#messageInput');
|
|
57
|
+
const nftToggle = this.element.querySelector('#nftToggle');
|
|
58
|
+
|
|
59
|
+
if (messageInput) {
|
|
60
|
+
this._messageInput = messageInput;
|
|
61
|
+
messageInput.addEventListener('input', this.handleMessageInput);
|
|
62
|
+
}
|
|
63
|
+
if (nftToggle) {
|
|
64
|
+
this._nftToggle = nftToggle;
|
|
65
|
+
nftToggle.addEventListener('change', this.handleNFTToggle);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
removeEventListeners() {
|
|
70
|
+
|
|
71
|
+
if (this._messageInput) {
|
|
72
|
+
this._messageInput.removeEventListener('input', this.handleMessageInput);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (this._nftToggle) {
|
|
76
|
+
this._nftToggle.removeEventListener('change', this.handleNFTToggle);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
validateTransaction() {
|
|
81
|
+
const isMessageValid = this.state.message.length <= 140;
|
|
82
|
+
const isNFTValid = !this.state.nftMintingEnabled || this.state.nftBalance < 10;
|
|
83
|
+
const newIsValid = isMessageValid && isNFTValid;
|
|
84
|
+
|
|
85
|
+
// Only emit if validation state has changed
|
|
86
|
+
if (this.state.lastValidationState !== newIsValid) {
|
|
87
|
+
this.state.lastValidationState = newIsValid;
|
|
88
|
+
this.state.isValid = newIsValid;
|
|
89
|
+
|
|
90
|
+
this.eventBus.emit('transactionValidation', {
|
|
91
|
+
isValid: newIsValid,
|
|
92
|
+
errors: this.getValidationErrors()
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
getValidationErrors() {
|
|
98
|
+
const errors = [];
|
|
99
|
+
|
|
100
|
+
if (this.state.message.length > 140) {
|
|
101
|
+
errors.push('Message must be 140 characters or less');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (this.state.nftMintingEnabled && this.state.nftBalance >= 10) {
|
|
105
|
+
errors.push('Cannot mint more than 10 NFTs');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return errors;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
updateElements() {
|
|
112
|
+
|
|
113
|
+
// Update message input if it's not focused
|
|
114
|
+
const messageInput = this.element.querySelector('#messageInput');
|
|
115
|
+
const nftToggle = this.element.querySelector('#nftToggle');
|
|
116
|
+
|
|
117
|
+
if (messageInput && !messageInput.matches(':focus')) {
|
|
118
|
+
messageInput.value = this.state.message;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (nftToggle) {
|
|
122
|
+
nftToggle.checked = this.state.nftMintingEnabled;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Re-attach event listeners after updating elements
|
|
126
|
+
this.removeEventListeners();
|
|
127
|
+
this.addEventListeners();
|
|
128
|
+
|
|
129
|
+
// Update character count
|
|
130
|
+
const characterCount = this.element.querySelector('.character-count');
|
|
131
|
+
if (characterCount) {
|
|
132
|
+
characterCount.textContent = `${this.state.message.length}/140`;
|
|
133
|
+
characterCount.className = `character-count ${this.state.message.length > 140 ? 'error' : ''}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Update validation status
|
|
137
|
+
const validationStatus = this.element.querySelector('.validation-status');
|
|
138
|
+
if (validationStatus) {
|
|
139
|
+
validationStatus.className = `validation-status ${this.state.isValid ? 'valid' : 'invalid'}`;
|
|
140
|
+
validationStatus.innerHTML = this.getValidationErrors()
|
|
141
|
+
.map(error => `<p class="error">${error}</p>`)
|
|
142
|
+
.join('');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
handleMessageInput(event) {
|
|
147
|
+
const newMessage = event.target.value || '';
|
|
148
|
+
|
|
149
|
+
// Update state without triggering a re-render
|
|
150
|
+
this.state = {
|
|
151
|
+
...this.state,
|
|
152
|
+
message: newMessage
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Update elements and ensure listeners
|
|
156
|
+
this.updateElements();
|
|
157
|
+
|
|
158
|
+
this.emitStateUpdate();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
handleNFTToggle(event) {
|
|
162
|
+
const isEnabled = event.target.checked;
|
|
163
|
+
|
|
164
|
+
// Update state without triggering a re-render
|
|
165
|
+
this.state = {
|
|
166
|
+
...this.state,
|
|
167
|
+
nftMintingEnabled: isEnabled
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// Update elements and ensure listeners
|
|
171
|
+
this.updateElements();
|
|
172
|
+
|
|
173
|
+
this.emitStateUpdate();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// New method to emit state updates
|
|
177
|
+
emitStateUpdate() {
|
|
178
|
+
const updatePayload = {
|
|
179
|
+
message: this.state.message,
|
|
180
|
+
nftMintingEnabled: this.state.nftMintingEnabled
|
|
181
|
+
};
|
|
182
|
+
this.eventBus.emit('transactionOptions:update', updatePayload);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
checkPhase2Status() {
|
|
186
|
+
const { isPhase2 } = this.state;
|
|
187
|
+
if (isPhase2 === null) {
|
|
188
|
+
// Phase not yet determined
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (this.state.isPhase2 !== isPhase2) {
|
|
192
|
+
this.setState({ isPhase2 });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
template() {
|
|
197
|
+
const { nftMintingEnabled, message, isValid, isPhase2, swapDirection } = this.state;
|
|
198
|
+
|
|
199
|
+
if (isPhase2 === null) {
|
|
200
|
+
return '';
|
|
201
|
+
}
|
|
202
|
+
if (isPhase2) {
|
|
203
|
+
return ''; // No options in Phase 2
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return `
|
|
207
|
+
<div class="transaction-options">
|
|
208
|
+
<div class="option-group ${swapDirection === 'sell' ? 'hidden' : ''}">
|
|
209
|
+
<label class="nft-toggle">
|
|
210
|
+
<input type="checkbox"
|
|
211
|
+
${nftMintingEnabled ? 'checked' : ''}
|
|
212
|
+
id="nftToggle">
|
|
213
|
+
Mint NFT with transaction
|
|
214
|
+
</label>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<div class="option-group">
|
|
218
|
+
<label for="messageInput">Transaction Message</label>
|
|
219
|
+
<textarea id="messageInput"
|
|
220
|
+
maxlength="140"
|
|
221
|
+
placeholder="Enter optional message..."
|
|
222
|
+
>${message}</textarea>
|
|
223
|
+
<span class="character-count ${message.length > 140 ? 'error' : ''}">
|
|
224
|
+
${message.length}/140
|
|
225
|
+
</span>
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
<div class="validation-status ${isValid ? 'valid' : 'invalid'}">
|
|
229
|
+
${this.getValidationErrors().map(error => `<p class="error">${error}</p>`).join('')}
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
render() {
|
|
236
|
+
return this.template();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { Component } from '@monygroupcorp/microact';
|
|
2
|
+
|
|
3
|
+
export class MessagePopup extends Component {
|
|
4
|
+
constructor(props) {
|
|
5
|
+
super(props);
|
|
6
|
+
this.state = {
|
|
7
|
+
messages: []
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
show(options) {
|
|
12
|
+
const { title = '', message = '', type = 'info', duration = 5000 } = options;
|
|
13
|
+
const id = Date.now() + Math.random();
|
|
14
|
+
|
|
15
|
+
const newMessage = { id, title, message, type };
|
|
16
|
+
this.setState({ messages: [...this.state.messages, newMessage] });
|
|
17
|
+
|
|
18
|
+
if (duration > 0) {
|
|
19
|
+
setTimeout(() => this.close(id), duration);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
close(id) {
|
|
24
|
+
const messages = this.state.messages.filter(msg => msg.id !== id);
|
|
25
|
+
this.setState({ messages });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
error(message, title = 'Error') {
|
|
29
|
+
this.show({ title, message, type: 'error' });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
success(message, title = 'Success') {
|
|
33
|
+
this.show({ title, message, type: 'success' });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
warning(message, title = 'Warning') {
|
|
37
|
+
this.show({ title, message, type: 'warning' });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
info(message, title = 'Info') {
|
|
41
|
+
this.show({ title, message, type: 'info' });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
render() {
|
|
45
|
+
return `
|
|
46
|
+
<div id="message-popup-container">
|
|
47
|
+
${this.state.messages.map(msg => `
|
|
48
|
+
<div class="message-popup ${msg.type}" data-id="${msg.id}">
|
|
49
|
+
<div class="message-content">
|
|
50
|
+
${msg.title ? `<div class="message-title">${msg.title}</div>` : ''}
|
|
51
|
+
<div class="message-text">${msg.message}</div>
|
|
52
|
+
</div>
|
|
53
|
+
<button class="close-button" data-id="${msg.id}">×</button>
|
|
54
|
+
</div>
|
|
55
|
+
`).join('')}
|
|
56
|
+
</div>
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
events() {
|
|
61
|
+
return {
|
|
62
|
+
'click .close-button': (e) => {
|
|
63
|
+
const id = parseFloat(e.target.dataset.id);
|
|
64
|
+
this.close(id);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static get styles() {
|
|
70
|
+
return `
|
|
71
|
+
#message-popup-container {
|
|
72
|
+
position: fixed;
|
|
73
|
+
top: 20px;
|
|
74
|
+
right: 20px;
|
|
75
|
+
z-index: 10000;
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
gap: 10px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.message-popup {
|
|
82
|
+
background: #ffffff;
|
|
83
|
+
border-radius: 8px;
|
|
84
|
+
padding: 15px 20px;
|
|
85
|
+
min-width: 300px;
|
|
86
|
+
max-width: 400px;
|
|
87
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
88
|
+
display: flex;
|
|
89
|
+
justify-content: space-between;
|
|
90
|
+
align-items: flex-start;
|
|
91
|
+
animation: slideIn 0.3s ease;
|
|
92
|
+
border-left: 4px solid #2196F3;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.message-popup.error {
|
|
96
|
+
border-left-color: #f44336;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.message-popup.success {
|
|
100
|
+
border-left-color: #4CAF50;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.message-popup.warning {
|
|
104
|
+
border-left-color: #ff9800;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.message-content {
|
|
108
|
+
flex-grow: 1;
|
|
109
|
+
margin-right: 10px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.message-title {
|
|
113
|
+
font-weight: bold;
|
|
114
|
+
margin-bottom: 5px;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.message-text {
|
|
118
|
+
font-size: 14px;
|
|
119
|
+
color: #666;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.close-button {
|
|
123
|
+
background: none;
|
|
124
|
+
border: none;
|
|
125
|
+
color: #999;
|
|
126
|
+
cursor: pointer;
|
|
127
|
+
font-size: 20px;
|
|
128
|
+
padding: 0;
|
|
129
|
+
line-height: 1;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.close-button:hover {
|
|
133
|
+
color: #666;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@keyframes slideIn {
|
|
137
|
+
from {
|
|
138
|
+
transform: translateX(100%);
|
|
139
|
+
opacity: 0;
|
|
140
|
+
}
|
|
141
|
+
to {
|
|
142
|
+
transform: translateX(0);
|
|
143
|
+
opacity: 1;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@keyframes fadeOut {
|
|
148
|
+
from {
|
|
149
|
+
transform: translateX(0);
|
|
150
|
+
opacity: 1;
|
|
151
|
+
}
|
|
152
|
+
to {
|
|
153
|
+
transform: translateX(100%);
|
|
154
|
+
opacity: 0;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Component } from '@monygroupcorp/microact';
|
|
2
|
+
|
|
3
|
+
export class WalletModal extends Component {
|
|
4
|
+
constructor(props) {
|
|
5
|
+
super(props);
|
|
6
|
+
this.providerMap = props.providerMap;
|
|
7
|
+
this.walletIcons = props.walletIcons;
|
|
8
|
+
this.onWalletSelected = props.onWalletSelected;
|
|
9
|
+
|
|
10
|
+
this.state = {
|
|
11
|
+
isVisible: false,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
events() {
|
|
16
|
+
return {
|
|
17
|
+
'click .wallet-modal-close': this.hide,
|
|
18
|
+
'click .wallet-modal-overlay': (e) => {
|
|
19
|
+
if (e.target === e.currentTarget) this.hide();
|
|
20
|
+
},
|
|
21
|
+
'click .wallet-option': async (e) => {
|
|
22
|
+
const walletType = e.currentTarget.dataset.wallet;
|
|
23
|
+
if (this.onWalletSelected) {
|
|
24
|
+
await this.onWalletSelected(walletType);
|
|
25
|
+
}
|
|
26
|
+
this.hide();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
show() {
|
|
32
|
+
this.setState({ isVisible: true });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
hide() {
|
|
36
|
+
this.setState({ isVisible: false });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
render() {
|
|
40
|
+
if (!this.state.isVisible) {
|
|
41
|
+
return '<div class="wallet-modal-container" style="display: none;"></div>';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const walletOptions = Object.keys(this.providerMap).map(walletType => `
|
|
45
|
+
<button class="wallet-option" data-wallet="${walletType}">
|
|
46
|
+
<img src="${this.walletIcons[walletType]}" alt="${walletType}">
|
|
47
|
+
<span>${walletType.charAt(0).toUpperCase() + walletType.slice(1)}</span>
|
|
48
|
+
</button>
|
|
49
|
+
`).join('');
|
|
50
|
+
|
|
51
|
+
return `
|
|
52
|
+
<div class="wallet-modal-container">
|
|
53
|
+
<div class="wallet-modal-overlay">
|
|
54
|
+
<div class="wallet-modal">
|
|
55
|
+
<div class="wallet-modal-header">
|
|
56
|
+
<h3>Select Your Wallet</h3>
|
|
57
|
+
<button class="wallet-modal-close">×</button>
|
|
58
|
+
</div>
|
|
59
|
+
<div class="wallet-options">
|
|
60
|
+
${walletOptions}
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default WalletModal;
|