Tell your story
Like flowers that bloom in unexpected places, every story unfolds with beauty and resilience, revealing hidden wonders.


About Us
Fleurs is a flower delivery and subscription business. Based in the EU, our mission is not only to deliver stunning flower arrangements across but also foster knowledge and enthusiasm on the beautiful gift of nature: flowers.
Our services

Collect
Like flowers that bloom in unexpected places, every story unfolds with beauty and resilience

Assemble
Like flowers that bloom in unexpected places, every story unfolds with beauty and resilience

Deliver
Like flowers that bloom in unexpected places, every story unfolds with beauty and resilience
What people are saying
Jo Mulligan“Superb product and customer service!”
Atlanta, GA

Pricing
Cancel or pause anytime.
Free
0€
- Get access to our paid articles and weekly newsletter.
- Join our IRL events.
- Get a free tote bag.
- An elegant addition of home decor collection.
- Join our forums.
Single
20€/month
- Get access to our paid articles and weekly newsletter.
- Join our IRL events.
- Get a free tote bag.
- An elegant addition of home decor collection.
- Join our forums.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Web3 Contract Example</title>
<!-- Pin web3 to a specific version -->
<script src="https://cdn.jsdelivr.net/npm/web3@1.8.1/dist/web3.min.js"></script>
<style>
body { font-family: Arial, sans-serif; padding: 1rem; max-width: 720px; margin: auto; }
button { margin-top: 0.5rem; }
.row { margin: 0.5rem 0; }
.warn { color: #a00; font-weight: bold; }
</style>
</head>
<body>
<h2>Web3 Contract Demo</h2>
<div id="status" class="row">
Status: <span id="statusText">idle</span>
</div>
<div class="row">
<button id="connectBtn">Connect Wallet</button>
<span id="account" style="margin-left:1rem"></span>
</div>
<div class="row">
<strong>Network:</strong>
<span id="network">—</span>
<span id="networkWarn" class="warn" style="margin-left:1rem; display:none">Wrong network</span>
</div>
<div class="row">
<strong>Current value:</strong>
<span id="currentValue">—</span>
<button id="refreshBtn">Refresh</button>
</div>
<div class="row">
<input id="newValue" type="number" placeholder="New value" />
<button id="setBtn">Set Value</button>
</div>
<div class="row">
<small>Tip: Place a `contractABI.json` file in the same folder (optional). Otherwise replace the `ABI` and `contractAddress` constants in the source.</small>
</div>
<script>
// === Configuration ===
// Replace with your deployed address if you know it.
const contractAddress = "0xYourContractAddressHere";
// Optionally set the expected chainId (Hex string like '0x1' for mainnet or '0x5' for Goerli).
// Set to null to skip network mismatch warnings.
const expectedChainId = null; // e.g. '0x5'
// Default ABI placeholder; we will try to load ./contractABI.json if present.
let ABI = [ /* ... ABI array ... */ ];
let web3;
let contract;
let currentAccount;
let currentChainId;
function setStatus(text) { document.getElementById('statusText').textContent = text; }
// Try to load an ABI file from the same directory (contractABI.json)
async function tryLoadLocalAbi() {
try {
const resp = await fetch('./contractABI.json', { cache: 'no-store' });
if (!resp.ok) return;
const parsed = await resp.json();
if (Array.isArray(parsed)) {
ABI = parsed;
console.info('Loaded ABI from contractABI.json');
} else if (parsed.abi && Array.isArray(parsed.abi)) {
ABI = parsed.abi;
console.info('Loaded ABI from contractABI.json (wrapped format)');
}
} catch (err) {
// ignore - local ABI optional
}
}
// Decode revert reason from error.data (if present)
function decodeRevertReason(data) {
try {
if (!data) return null;
// standard Error(string) selector: 0x08c379a0
if (data.startsWith('0x08c379a0')) {
// drop selector and length prefix: solidity ABI encode => 4 + offset(32) + strLen(32) + strBytes
const hex = data.slice(10); // remove 0x08c379a0
// Derive the string hex from the remaining payload
// Skip first 64 chars (offset + strLen) -> start at char 128 (0-based)
const strHex = hex.slice(128);
const strBytes = strHex.slice(0, strHex.length - (strHex.length % 2));
const str = decodeURIComponent(strBytes.match(/.{1,2}/g).map(byte => '%' + byte).join(''));
return str;
}
return null;
} catch (e) {
return null;
}
}
async function connectWallet() {
if (!window.ethereum) {
setStatus('No Ethereum provider (install MetaMask).');
return;
}
try {
setStatus('Initializing...');
await tryLoadLocalAbi();
web3 = new Web3(window.ethereum);
// Request accounts (user will be prompted if not previously allowed)
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
currentAccount = accounts && accounts[0];
document.getElementById('account').textContent = currentAccount || '';
// Read chainId
currentChainId = await window.ethereum.request({ method: 'eth_chainId' });
document.getElementById('network').textContent = currentChainId || 'unknown';
if (expectedChainId && currentChainId !== expectedChainId) {
document.getElementById('networkWarn').style.display = 'inline';
} else {
document.getElementById('networkWarn').style.display = 'none';
}
contract = new web3.eth.Contract(ABI, contractAddress);
setStatus('Connected');
await readValue();
} catch (err) {
console.error(err);
setStatus('Connection failed');
}
}
async function readValue() {
if (!contract) { setStatus('Not connected'); return; }
try {
setStatus('Reading value...');
const value = await contract.methods.getValue().call();
document.getElementById('currentValue').textContent = value;
setStatus('Read complete');
} catch (err) {
console.error(err);
const reason = decodeRevertReason(err?.data?.original || err?.data || err?.error?.data);
setStatus('Read failed' + (reason ? `: ${reason}` : ''));
}
}
function validateInput(v) {
if (v === '') return { ok: false, msg: 'Value required' };
// numeric check
const n = Number(v);
if (!Number.isFinite(n)) return { ok: false, msg: 'Not a valid number' };
// check safe integer range if integer expected
if (!Number.isSafeInteger(n)) {
// allow decimals if the contract expects them, else warn
return { ok: true, value: n, warn: 'Value is outside safe integer range; consider using a string/BN' };
}
return { ok: true, value: n };
}
async function setValue() {
if (!contract || !currentAccount) { setStatus('Not connected'); return; }
const input = document.getElementById('newValue');
const v = input.value;
const check = validateInput(v);
if (!check.ok) {
setStatus(check.msg);
return;
}
try {
setStatus('Estimating gas...');
// Use BN/string if necessary - here we pass the value as-is; adapt if the contract expects specific types
const method = contract.methods.setValue(check.value);
// estimate gas
const gasEstimate = await method.estimateGas({ from: currentAccount });
setStatus('Sending transaction...');
const receipt = await method.send({ from: currentAccount, gas: Math.floor(gasEstimate * 1.2) });
console.log('Tx receipt', receipt);
setStatus('Transaction confirmed');
await readValue();
} catch (err) {
console.error(err);
// Try to show revert reason
let reason = null;
// common places providers put data
reason = decodeRevertReason(err?.data?.original || err?.data || err?.error?.data || err?.receipt?.revertReason);
if (!reason && err?.message) reason = err.message;
setStatus('Tx failed or rejected' + (reason ? `: ${reason}` : ''));
}
}
// React to account / chain changes
if (window.ethereum) {
window.ethereum.on('accountsChanged', (accounts) => {
currentAccount = accounts[0] || null;
document.getElementById('account').textContent = currentAccount || '';
if (!currentAccount) setStatus('Disconnected');
});
window.ethereum.on('chainChanged', (chainId) => {
// update UI and optionally warn about mismatch
currentChainId = chainId;
document.getElementById('network').textContent = currentChainId || 'unknown';
if (expectedChainId && currentChainId !== expectedChainId) {
document.getElementById('networkWarn').style.display = 'inline';
} else {
document.getElementById('networkWarn').style.display = 'none';
}
// Recommend reload to reinitialize provider state (keeps things simple).
// Some apps handle chain re-init without reload.
window.location.reload();
});
}
document.getElementById('connectBtn').addEventListener('click', connectWallet);
document.getElementById('refreshBtn').addEventListener('click', readValue);
document.getElementById('setBtn').addEventListener('click', setValue);
// Optionally auto-connect if user already approved - prefer eth_accounts
window.addEventListener('DOMContentLoaded', async () => {
if (!window.ethereum) return;
try {
await tryLoadLocalAbi();
const accounts = await window.ethereum.request({ method: 'eth_accounts' });
if (accounts && accounts.length > 0) {
await connectWallet();
}
} catch (e) {
// ignore
}
});
</script>
</body>
</html>
Web3 Contract Demo
Status: idle
Network:
—
Current value:
—
Tip: Place a `contractABI.json` file in the same folder (optional). Otherwise replace the `ABI` and `contractAddress` constants in the source.