@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
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@monygroupcorp/micro-web3",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A lean, reusable Web3 toolkit with components for wallet connection, IPFS, and common Web3 UI patterns.",
|
|
5
|
+
"main": "dist/micro-web3.cjs.js",
|
|
6
|
+
"module": "dist/micro-web3.esm.js",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "rollup -c",
|
|
10
|
+
"clean": "rm -rf dist"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"web3",
|
|
14
|
+
"wallet",
|
|
15
|
+
"ipfs",
|
|
16
|
+
"react",
|
|
17
|
+
"minimal",
|
|
18
|
+
"framework"
|
|
19
|
+
],
|
|
20
|
+
"author": "arthurt <mony.group.corporation@gmail.com>",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@monygroupcorp/microact": ">=0.1.0"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"ethers": "^5.7.2"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
30
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
31
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
32
|
+
"rollup": "^4.55.1"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const { nodeResolve } = require('@rollup/plugin-node-resolve');
|
|
2
|
+
const commonjs = require('@rollup/plugin-commonjs');
|
|
3
|
+
const terser = require('@rollup/plugin-terser');
|
|
4
|
+
|
|
5
|
+
module.exports = [
|
|
6
|
+
{
|
|
7
|
+
input: 'src/index.js',
|
|
8
|
+
output: [
|
|
9
|
+
{
|
|
10
|
+
file: 'dist/micro-web3.cjs.js',
|
|
11
|
+
format: 'cjs',
|
|
12
|
+
sourcemap: true,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
file: 'dist/micro-web3.esm.js',
|
|
16
|
+
format: 'esm',
|
|
17
|
+
sourcemap: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
file: 'dist/micro-web3.umd.js',
|
|
21
|
+
format: 'umd',
|
|
22
|
+
name: 'microWeb3',
|
|
23
|
+
sourcemap: true,
|
|
24
|
+
globals: {
|
|
25
|
+
'@monygroupcorp/microact': 'microact'
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
plugins: [
|
|
30
|
+
nodeResolve(),
|
|
31
|
+
commonjs(),
|
|
32
|
+
terser()
|
|
33
|
+
],
|
|
34
|
+
external: ['@monygroupcorp/microact']
|
|
35
|
+
}
|
|
36
|
+
];
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { Component } from '@monygroupcorp/microact';
|
|
2
|
+
|
|
3
|
+
export class BondingCurve extends Component {
|
|
4
|
+
constructor(props) {
|
|
5
|
+
super(props);
|
|
6
|
+
this.eventBus = props.eventBus;
|
|
7
|
+
this.state = {
|
|
8
|
+
currentPrice: props.price || 0,
|
|
9
|
+
totalBondingSupply: props.contractData?.totalBondingSupply || 0,
|
|
10
|
+
totalMessages: props.contractData?.totalMessages || 0,
|
|
11
|
+
totalNFTs: props.contractData?.totalNFTs || 0,
|
|
12
|
+
dataReady: props.dataReady || false,
|
|
13
|
+
contractEthBalance: props.contractData?.contractEthBalance || 0,
|
|
14
|
+
liquidityPool: props.contractData?.liquidityPool || null,
|
|
15
|
+
chainId: props.chainId || null
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
onMount() {
|
|
20
|
+
this.unsubscribeEvents = [
|
|
21
|
+
this.eventBus.on('contractData:updated', (data) => this.updateProps({ contractData: data })),
|
|
22
|
+
this.eventBus.on('price:updated', (data) => this.updateProps({ price: data.price }))
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
this.resizeHandler = () => requestAnimationFrame(() => this.drawCurve());
|
|
26
|
+
window.addEventListener('resize', this.resizeHandler);
|
|
27
|
+
|
|
28
|
+
if (this.isLiquidityDeployed()) {
|
|
29
|
+
this.renderDexToolsChart();
|
|
30
|
+
} else {
|
|
31
|
+
this.drawCurve();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
updateProps(newProps) {
|
|
36
|
+
const newState = { ...this.state };
|
|
37
|
+
let shouldUpdate = false;
|
|
38
|
+
|
|
39
|
+
if (newProps.contractData) {
|
|
40
|
+
const { contractData } = newProps;
|
|
41
|
+
newState.totalBondingSupply = contractData.totalBondingSupply;
|
|
42
|
+
newState.totalMessages = contractData.totalMessages;
|
|
43
|
+
newState.totalNFTs = contractData.totalNFTs;
|
|
44
|
+
newState.contractEthBalance = contractData.contractEthBalance;
|
|
45
|
+
if (newState.liquidityPool !== contractData.liquidityPool) {
|
|
46
|
+
newState.liquidityPool = contractData.liquidityPool;
|
|
47
|
+
shouldUpdate = true; // Phase changed
|
|
48
|
+
}
|
|
49
|
+
newState.dataReady = true;
|
|
50
|
+
shouldUpdate = true;
|
|
51
|
+
}
|
|
52
|
+
if (newProps.price !== undefined) {
|
|
53
|
+
newState.currentPrice = newProps.price;
|
|
54
|
+
shouldUpdate = true;
|
|
55
|
+
}
|
|
56
|
+
if (newProps.chainId !== undefined) {
|
|
57
|
+
newState.chainId = newProps.chainId;
|
|
58
|
+
shouldUpdate = true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this.setState(newState);
|
|
62
|
+
|
|
63
|
+
if (shouldUpdate) {
|
|
64
|
+
if (this.isLiquidityDeployed()) {
|
|
65
|
+
this.renderDexToolsChart();
|
|
66
|
+
} else {
|
|
67
|
+
this.drawCurve();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
isLiquidityDeployed() {
|
|
73
|
+
const { liquidityPool } = this.state;
|
|
74
|
+
return liquidityPool && liquidityPool !== '0x0000000000000000000000000000000000000000';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
renderDexToolsChart() {
|
|
78
|
+
try {
|
|
79
|
+
const container = this.element.querySelector('.bonding-curve');
|
|
80
|
+
if (!container) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Clear any existing content
|
|
85
|
+
container.innerHTML = '';
|
|
86
|
+
|
|
87
|
+
// Construct the DEXTools URL dynamically
|
|
88
|
+
const networkName = this.getNetworkName(this.state.chainId);
|
|
89
|
+
const chartUrl = `https://www.dextools.io/widget-chart/en/${networkName}/pe-light/${this.state.liquidityPool}?theme=dark&chartType=2&chartResolution=30&drawingToolbars=false`;
|
|
90
|
+
|
|
91
|
+
// Use exact DEXTools iframe format from share button
|
|
92
|
+
// Note: This will fail on localhost due to X-Frame-Options, but that's expected
|
|
93
|
+
container.innerHTML = `
|
|
94
|
+
<iframe
|
|
95
|
+
id="dextools-widget"
|
|
96
|
+
title="$EXEC DEXTools Trading Chart"
|
|
97
|
+
width="100%"
|
|
98
|
+
height="100%"
|
|
99
|
+
src="${chartUrl}"
|
|
100
|
+
style="border: none;"
|
|
101
|
+
onerror="console.warn('DEXTools chart failed to load (expected on localhost)')"
|
|
102
|
+
></iframe>
|
|
103
|
+
`;
|
|
104
|
+
|
|
105
|
+
// Add error handler for iframe load failures (expected on localhost)
|
|
106
|
+
const iframe = container.querySelector('#dextools-widget');
|
|
107
|
+
if (iframe) {
|
|
108
|
+
iframe.onerror = () => {
|
|
109
|
+
console.warn('DEXTools chart iframe failed to load (expected on localhost due to X-Frame-Options)');
|
|
110
|
+
// Show a fallback message instead of breaking
|
|
111
|
+
container.innerHTML = `
|
|
112
|
+
<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #888;">
|
|
113
|
+
<div>Chart will be available in production</div>
|
|
114
|
+
</div>
|
|
115
|
+
`;
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error('Error rendering DEXTools chart:', error);
|
|
120
|
+
// Don't let chart errors break the component
|
|
121
|
+
const container = this.element.querySelector('.bonding-curve');
|
|
122
|
+
if (container) {
|
|
123
|
+
container.innerHTML = `
|
|
124
|
+
<div style="display: flex; align-items: center; justify-content: center; height: 100%; color: #888;">
|
|
125
|
+
<div>Chart unavailable (expected on localhost)</div>
|
|
126
|
+
</div>
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
getNetworkName(chainId) {
|
|
133
|
+
const networks = {
|
|
134
|
+
1: 'ether',
|
|
135
|
+
5: 'goerli',
|
|
136
|
+
// Add more networks as needed
|
|
137
|
+
};
|
|
138
|
+
return networks[chainId] || 'ether';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
drawCurve() {
|
|
142
|
+
const canvas = this.element.querySelector('#curveChart');
|
|
143
|
+
if (!canvas) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const ctx = canvas.getContext('2d');
|
|
148
|
+
|
|
149
|
+
canvas.width = canvas.offsetWidth;
|
|
150
|
+
canvas.height = canvas.offsetHeight;
|
|
151
|
+
|
|
152
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
153
|
+
this.drawBaseCurve(ctx, canvas);
|
|
154
|
+
|
|
155
|
+
if (this.state.dataReady) {
|
|
156
|
+
this.drawDataElements(ctx);
|
|
157
|
+
} else {
|
|
158
|
+
this.drawPlaceholderLabels(ctx);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
drawBaseCurve(ctx, canvas) {
|
|
163
|
+
const curveColor = getComputedStyle(document.documentElement).getPropertyValue('--bonding-curve-color').trim() || '#764ba2';
|
|
164
|
+
ctx.strokeStyle = curveColor;
|
|
165
|
+
ctx.lineWidth = 2;
|
|
166
|
+
|
|
167
|
+
const curve = (x) => Math.pow(0.1 + (x * 0.8), 3.5);
|
|
168
|
+
const points = this.calculateCurvePoints(canvas, curve);
|
|
169
|
+
this.drawCurvePath(ctx, points);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
drawDataElements(ctx) {
|
|
173
|
+
const curve = (x) => Math.pow(0.1 + (x * 0.8), 3.5);
|
|
174
|
+
const maxPrice = 0.08;
|
|
175
|
+
const currentPosition = Math.max(0.1, Math.min(0.9, Math.pow(this.state.currentPrice / maxPrice, 1/3.5)));
|
|
176
|
+
const points = this.calculateCurvePoints(this.element.querySelector('#curveChart'), curve);
|
|
177
|
+
this.drawCurrentPosition(ctx, points, currentPosition);
|
|
178
|
+
this.drawLabels(ctx);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
drawPlaceholderLabels(ctx) {
|
|
182
|
+
ctx.fillStyle = '#666666';
|
|
183
|
+
ctx.font = '12px Courier New';
|
|
184
|
+
ctx.fillText('Loading price data...', 10, 20);
|
|
185
|
+
ctx.fillText('Loading supply data...', 10, 40);
|
|
186
|
+
ctx.fillText('Loading NFT data...', 10, 60);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
calculateCurvePoints(canvas, curve) {
|
|
190
|
+
const points = [];
|
|
191
|
+
const numPoints = 100;
|
|
192
|
+
for (let i = 0; i <= numPoints; i++) {
|
|
193
|
+
const x = i / numPoints;
|
|
194
|
+
const y = curve(x);
|
|
195
|
+
const canvasX = x * canvas.width * 0.8 + canvas.width * 0.1;
|
|
196
|
+
const canvasY = canvas.height * 0.9 - y * canvas.height * 0.8;
|
|
197
|
+
points.push({ x: canvasX, y: canvasY });
|
|
198
|
+
}
|
|
199
|
+
return points;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
drawCurvePath(ctx, points) {
|
|
203
|
+
ctx.beginPath();
|
|
204
|
+
ctx.moveTo(points[0].x, points[0].y);
|
|
205
|
+
for (let i = 1; i < points.length; i++) {
|
|
206
|
+
ctx.lineTo(points[i].x, points[i].y);
|
|
207
|
+
}
|
|
208
|
+
ctx.stroke();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
drawCurrentPosition(ctx, points, currentPosition) {
|
|
212
|
+
const segmentSize = 0.05;
|
|
213
|
+
const startIndex = Math.floor((currentPosition - segmentSize/2) * points.length);
|
|
214
|
+
const endIndex = Math.floor((currentPosition + segmentSize/2) * points.length);
|
|
215
|
+
|
|
216
|
+
if (startIndex >= 0 && endIndex < points.length) {
|
|
217
|
+
const curveColor = getComputedStyle(document.documentElement).getPropertyValue('--bonding-curve-color').trim() || '#764ba2';
|
|
218
|
+
ctx.beginPath();
|
|
219
|
+
ctx.strokeStyle = curveColor;
|
|
220
|
+
ctx.lineWidth = 4;
|
|
221
|
+
|
|
222
|
+
ctx.moveTo(points[startIndex].x, points[startIndex].y);
|
|
223
|
+
for (let i = startIndex; i <= endIndex; i++) {
|
|
224
|
+
ctx.lineTo(points[i].x, points[i].y);
|
|
225
|
+
}
|
|
226
|
+
ctx.stroke();
|
|
227
|
+
|
|
228
|
+
const centerIndex = Math.floor((startIndex + endIndex) / 2);
|
|
229
|
+
const centerPoint = points[centerIndex];
|
|
230
|
+
|
|
231
|
+
ctx.beginPath();
|
|
232
|
+
ctx.arc(centerPoint.x, centerPoint.y, 4, 0, Math.PI * 2);
|
|
233
|
+
ctx.fillStyle = '#FF0000';
|
|
234
|
+
ctx.fill();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
drawLabels(ctx) {
|
|
239
|
+
ctx.fillStyle = '#666666';
|
|
240
|
+
ctx.font = '12px Courier New';
|
|
241
|
+
ctx.fillText(`${this.state.currentPrice.toFixed(4)} ETH / EXEC`, 10, 20);
|
|
242
|
+
ctx.fillText(`Total Supply: ${this.state.totalBondingSupply.toLocaleString()} EXEC`, 10, 40);
|
|
243
|
+
ctx.fillText(`Total Messages: ${this.state.totalMessages.toLocaleString()}`, 10, 60);
|
|
244
|
+
ctx.fillText(`Total NFTs: ${this.state.totalNFTs.toLocaleString()}`, 10, 80);
|
|
245
|
+
ctx.fillText(`Contract ETH Balance: ${this.state.contractEthBalance}`, 10, 100);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
unmount() {
|
|
249
|
+
if (this.unsubscribeEvents) {
|
|
250
|
+
this.unsubscribeEvents.forEach(unsubscribe => unsubscribe());
|
|
251
|
+
}
|
|
252
|
+
if (this.resizeHandler) {
|
|
253
|
+
window.removeEventListener('resize', this.resizeHandler);
|
|
254
|
+
}
|
|
255
|
+
super.unmount();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
render() {
|
|
259
|
+
return `
|
|
260
|
+
<div class="bonding-curve marble-bg">
|
|
261
|
+
${this.isLiquidityDeployed() ? '' : '<canvas id="curveChart"></canvas>'}
|
|
262
|
+
</div>
|
|
263
|
+
`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
static get styles() {
|
|
267
|
+
return `
|
|
268
|
+
.bonding-curve {
|
|
269
|
+
width: 100%;
|
|
270
|
+
height: 90%;
|
|
271
|
+
min-height: 400px;
|
|
272
|
+
background: #1a1a1a;
|
|
273
|
+
border-radius: 8px;
|
|
274
|
+
padding: 0px;
|
|
275
|
+
overflow: hidden;
|
|
276
|
+
position: relative;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
#curveChart {
|
|
280
|
+
width: 100%;
|
|
281
|
+
height: 100%;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
#dextools-widget {
|
|
285
|
+
width: 100%;
|
|
286
|
+
height: 100%;
|
|
287
|
+
position: absolute;
|
|
288
|
+
top: 0;
|
|
289
|
+
left: 0;
|
|
290
|
+
border: none;
|
|
291
|
+
}
|
|
292
|
+
`;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export default BondingCurve;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Component } from '@monygroupcorp/microact';
|
|
2
|
+
|
|
3
|
+
export class BalanceDisplay extends Component {
|
|
4
|
+
constructor(props) {
|
|
5
|
+
super(props);
|
|
6
|
+
this.eventBus = props.eventBus;
|
|
7
|
+
this.state = {
|
|
8
|
+
balances: props.balances || { eth: '0', exec: '0', lastUpdated: null },
|
|
9
|
+
loading: props.loading !== undefined ? props.loading : true,
|
|
10
|
+
error: props.error || null,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
this.handleBalanceUpdate = this.handleBalanceUpdate.bind(this);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
template() {
|
|
17
|
+
const { balances, loading, error } = this.state;
|
|
18
|
+
|
|
19
|
+
return `
|
|
20
|
+
<div class="balance-display marble-bg ${loading ? 'loading' : ''} ${error ? 'error' : ''}">
|
|
21
|
+
<div class="balance-header">
|
|
22
|
+
<h3>Your Balances</h3>
|
|
23
|
+
${balances.lastUpdated ? `
|
|
24
|
+
<span class="last-updated">
|
|
25
|
+
Last updated: ${new Date(balances.lastUpdated).toLocaleTimeString()}
|
|
26
|
+
</span>
|
|
27
|
+
` : ''}
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="balance-content">
|
|
31
|
+
${loading ? `
|
|
32
|
+
<div class="loading-indicator">Loading balances...</div>
|
|
33
|
+
` : error ? `
|
|
34
|
+
<div class="error-message">${error}</div>
|
|
35
|
+
` : `
|
|
36
|
+
<div class="balance-grid">
|
|
37
|
+
<div class="balance-item eth">
|
|
38
|
+
<span class="label">ETH Balance</span>
|
|
39
|
+
<span class="amount">${balances.eth}</span>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="balance-item exec">
|
|
42
|
+
<span class="label">EXEC Balance</span>
|
|
43
|
+
<span class="amount">${balances.exec}</span>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
`}
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
render() {
|
|
53
|
+
return this.template();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
onMount() {
|
|
57
|
+
if (this.eventBus) {
|
|
58
|
+
this.unsubscribeEvents = [
|
|
59
|
+
this.eventBus.on('balances:updated', this.handleBalanceUpdate)
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
onUnmount() {
|
|
65
|
+
if (this.unsubscribeEvents) {
|
|
66
|
+
this.unsubscribeEvents.forEach(unsubscribe => unsubscribe());
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
updateProps(newProps) {
|
|
71
|
+
this.setState({
|
|
72
|
+
balances: newProps.balances !== undefined ? newProps.balances : this.state.balances,
|
|
73
|
+
loading: newProps.loading !== undefined ? newProps.loading : this.state.loading,
|
|
74
|
+
error: newProps.error !== undefined ? newProps.error : this.state.error,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
handleBalanceUpdate(balances) {
|
|
79
|
+
this.setState({ balances, loading: false, error: null });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { Component } from '@monygroupcorp/microact';
|
|
2
|
+
|
|
3
|
+
// Add event name constants
|
|
4
|
+
const EVENTS = {
|
|
5
|
+
UPDATE: {
|
|
6
|
+
PRICE: 'price:updated',
|
|
7
|
+
STATUS: 'price:status'
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export class PriceDisplay extends Component {
|
|
12
|
+
constructor(props) {
|
|
13
|
+
super(props);
|
|
14
|
+
this.eventBus = props.eventBus;
|
|
15
|
+
this.state = {
|
|
16
|
+
price: props.price || 0,
|
|
17
|
+
lastUpdated: props.lastUpdated || null,
|
|
18
|
+
loading: props.loading !== undefined ? props.loading : true,
|
|
19
|
+
error: props.error || null
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
this.handlePriceUpdate = this.handlePriceUpdate.bind(this);
|
|
23
|
+
this.handleStatusUpdate = this.handleStatusUpdate.bind(this);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
onMount() {
|
|
27
|
+
if (this.eventBus) {
|
|
28
|
+
this.eventBus.on(EVENTS.UPDATE.PRICE, this.handlePriceUpdate);
|
|
29
|
+
this.eventBus.on(EVENTS.UPDATE.STATUS, this.handleStatusUpdate);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
onUnmount() {
|
|
34
|
+
if (this.eventBus) {
|
|
35
|
+
this.eventBus.off(EVENTS.UPDATE.PRICE, this.handlePriceUpdate);
|
|
36
|
+
this.eventBus.off(EVENTS.UPDATE.STATUS, this.handleStatusUpdate);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
updateProps(newProps) {
|
|
41
|
+
this.setState({
|
|
42
|
+
price: newProps.price !== undefined ? newProps.price : this.state.price,
|
|
43
|
+
lastUpdated: newProps.lastUpdated !== undefined ? newProps.lastUpdated : this.state.lastUpdated,
|
|
44
|
+
loading: newProps.loading !== undefined ? newProps.loading : this.state.loading,
|
|
45
|
+
error: newProps.error !== undefined ? newProps.error : this.state.error
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
setState(newState) {
|
|
50
|
+
super.setState(newState);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
handlePriceUpdate({ price }) {
|
|
54
|
+
this.setState({
|
|
55
|
+
price: price || 0,
|
|
56
|
+
loading: false
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
handleStatusUpdate({ loading, error }) {
|
|
61
|
+
this.setState({ loading, error });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
template() {
|
|
65
|
+
const { price, lastUpdated, loading, error } = this.state;
|
|
66
|
+
|
|
67
|
+
if (loading) return `
|
|
68
|
+
<div class="price-display loading marble-bg">
|
|
69
|
+
<div class="price-header">
|
|
70
|
+
<h3>Cult Exec </h3>
|
|
71
|
+
<h3>Bonding Curve Presale</h3>
|
|
72
|
+
</div>
|
|
73
|
+
<div class="price-content">
|
|
74
|
+
<div class="loading-indicator">Loading...</div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>`;
|
|
77
|
+
|
|
78
|
+
if (error) return `
|
|
79
|
+
<div class="price-display error marble-bg">
|
|
80
|
+
<div class="price-header">
|
|
81
|
+
<h3>Cult Exec </h3>
|
|
82
|
+
<h3>Bonding Curve Presale</h3>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="price-content">
|
|
85
|
+
<div class="error-message">Error: ${error}</div>
|
|
86
|
+
</div>
|
|
87
|
+
</div>`;
|
|
88
|
+
|
|
89
|
+
return `
|
|
90
|
+
<div class="price-display marble-bg">
|
|
91
|
+
<div class="price-header">
|
|
92
|
+
<h3>Cult Exec </h3>
|
|
93
|
+
<h3>Bonding Curve Presale</h3>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="price-content">
|
|
97
|
+
<div class="price-value">
|
|
98
|
+
<span class="amount">${Number(price).toFixed(8)}</span>
|
|
99
|
+
<span class="currency">ETH / Cult Exec</span>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
render() {
|
|
107
|
+
const result = this.template();
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Add component styles
|
|
112
|
+
static get styles() {
|
|
113
|
+
return `
|
|
114
|
+
.price-display {
|
|
115
|
+
background: #1a1a1a;
|
|
116
|
+
border-radius: 8px;
|
|
117
|
+
padding: 16px;
|
|
118
|
+
margin-bottom: 16px;
|
|
119
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.price-header {
|
|
123
|
+
display: flex;
|
|
124
|
+
justify-content: space-between;
|
|
125
|
+
align-items: center;
|
|
126
|
+
margin-bottom: 12px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.price-header h3 {
|
|
130
|
+
margin: 0;
|
|
131
|
+
color: var(--price-display-header-color, var(--color-text-primary, #333));
|
|
132
|
+
font-size: 16px;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.last-updated {
|
|
136
|
+
color: #666;
|
|
137
|
+
font-size: 12px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.price-content {
|
|
141
|
+
display: flex;
|
|
142
|
+
justify-content: center;
|
|
143
|
+
align-items: center;
|
|
144
|
+
min-height: 48px;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.price-value {
|
|
148
|
+
font-size: 24px;
|
|
149
|
+
font-weight: bold;
|
|
150
|
+
color: #fff;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.amount {
|
|
154
|
+
margin-right: 8px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.currency {
|
|
158
|
+
color: var(--price-display-currency-color, var(--color-accent, #764ba2));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.loading-indicator {
|
|
162
|
+
color: #666;
|
|
163
|
+
font-style: italic;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.error-message {
|
|
167
|
+
color: #ff4444;
|
|
168
|
+
font-size: 14px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* Loading state animation */
|
|
172
|
+
.price-display.loading .price-content {
|
|
173
|
+
opacity: 0.7;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.price-display.loading .loading-indicator:after {
|
|
177
|
+
content: '';
|
|
178
|
+
animation: loading-dots 1.5s infinite;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@keyframes loading-dots {
|
|
182
|
+
0% { content: '.'; }
|
|
183
|
+
33% { content: '..'; }
|
|
184
|
+
66% { content: '...'; }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* Error state styles */
|
|
188
|
+
.price-display.error {
|
|
189
|
+
border: 1px solid #ff4444;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* Mobile responsive styles */
|
|
193
|
+
@media (max-width: 768px) {
|
|
194
|
+
.price-display {
|
|
195
|
+
margin: 8px;
|
|
196
|
+
padding: 12px;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.price-header {
|
|
200
|
+
flex-direction: column;
|
|
201
|
+
align-items: flex-start;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.last-updated {
|
|
205
|
+
margin-top: 4px;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.price-value {
|
|
209
|
+
font-size: 20px;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
`;
|
|
213
|
+
}
|
|
214
|
+
}
|