CarbonX — Carbon Credit Token Marketplace
Prototype
Experiment · Blockchain

CarbonX — Carbon Credit Token Marketplace

2023

A carbon-credit token exchange taken as far as a full design pass and a working token layer, and no further. The front end draws most of the screens the product would need — dashboard, token creation, transactions, profile and a Leaflet emissions map — over hard-coded sample data with nothing behind it, which was the point: find out whether the idea survived contact with its own UI before writing anything real. The marketplace is the one screen that never got cut over — it still lists shoes at $19.99 from the template it came out of, which for a marketplace product is the tell. Beside it sits a Next.js Solana dApp carrying the same CarbonX branding, which mints an SPL token, its associated account and its Metaplex metadata in a single signature, publishing the metadata JSON and logo to Arweave through Bundlr first so a token can never exist half-described. What the design pass exposed was that everything left — who certifies a removal, what settlement means between two parties under different regulators — was not a front-end problem, and the idea came back later with a real backend as the Green Credit work.

Built with
HTMLHTML
CSSCSS
JavaScriptJavaScript
BootstrapBootstrap
jQueryjQuery
LeafletLeaflet
GeoJSONGeoJSON
CanvasJSCanvasJS
Next.jsNext.js
TypeScriptTypeScript
Solana Web3.jsSolana Web3.js
SPL TokenSPL Token
MetaplexMetaplex
Arweave / BundlrArweave / Bundlr
TailwindTailwind
DockerDocker
Project Details
RESOURCES

STATUS
Prototype
YEAR

2023

TYPE

Experiment · Blockchain

TAGS
Web3
Sustainability
Prototype
Solana

A front-end study of what a carbon-credit exchange would look like if one existed, and the one part of it that got built for real. The design pass has no server and no persistence behind it. The point was to draw every screen the product would need and find out whether the idea survived contact with its own UI before writing anything real.

The pitch is easy to say out loud. Emitters buy credits from projects that remove carbon, the credits are tokens, a marketplace matches the two sides. Every noun in that sentence hides a rule nobody had written down: what a credit is worth, who certifies that the removal actually happened, what settlement means between two parties under different regulators. Drawing the screens was the cheapest available way to find out which of those questions the design was quietly assuming away, and it turned out to be most of them. The exception is the token itself, which is a well-specified problem with a chain underneath it, and that half exists as a working Solana dApp built beside the mock-up.

What got built

Two halves of a design pass. carbonx/ is the public face: a Bootstrap and jQuery site with home, about, services, why-us, team and login pages. The root directory holds the application shell, a fixed icon sidebar over five screens, all hand-written HTML and CSS with no framework. Beside them sits a Next.js token layer built on the Solana dApp scaffold, with its own wallet context and app shell.

  • Dashboard with an expenditure-versus-income panel, a wallet balance and a last-five transactions list.
  • Create token form taking a token name, unit value, initial supply and an icon URL.
  • Marketplace and transactions screens for browsing and settling credit listings.
  • Profile page with the account identity and holdings.
  • Emissions map built on Leaflet over OpenStreetMap tiles, sizing a circleMarker per country by its emissions figure and binding a MtCO2 popup to each one.
  • countries.geojson sitting alongside it with proper per-country coordinates and emissions, ready to be swapped in for the placeholder markers.
  • Operator overview in the root index.html: a collapsible icon rail over Dashboard, History, Reports, Industries, Profile and Settings, three counters reading 36 transactions, 13 industries and 7,000 credits, and a Buyer/Seller/Credits/Status table listing trades between named Indian industrials.
  • Income versus expenditure chart drawn with CanvasJS in the dark2 theme, two column series over five months, sitting next to a wallet panel.
  • Wallet connection on the token side, through wallet-adapter with Phantom, Solflare, Sollet, the Sollet extension and Torus, plus an auto-connect preference held in its own context provider.
  • Arweave metadata upload using a WebBundlr client that can target either devnet.bundlr.network or node1.bundlr.network, funded from the connected wallet's SOL, uploading the image first and then a JSON document that points at it.
  • Metadata reader that derives the metadata PDA for any mint address, pulls the account with getAccountInfo, runs Metadata.deserialize, then fetches the off-chain URI and renders the token's logo from its image field.
  • Metadata updater built on createUpdateMetadataAccountV2Instruction for tokens whose update authority you hold, plus a devnet airdrop, a live SOL balance in a zustand store, and sign-message and send-transaction panels for checking a wallet behaves before you spend anything.
  • Five Next pages - home, a basics playground, the uploader, the metadata reader and the updater - each a thin page over a view component, so the token flow is walked in the order the chain needs it.

Two shells that never met

The most telling thing in the mock-up is that there are two different application chromes and neither links to the other. index.html is a dark operator console with a search bar in the header and an icon rail that collapses when menuicn is clicked. home.html, market.html, createtoken.html, transactions.html and profile.html all share a different chrome: a black 200-pixel left navigation with a user block and a logout button pinned to the bottom. Every link in both navigations points at #. The two halves were drawn by different hands on the same afternoon and were never wired into one another, which is exactly the failure mode of a design pass with no router underneath it.

The currency story has the same seam. transactions.html denominates a transfer as 5 SOL, which puts the settlement layer on Solana; profile.html shows a wallet address of 0x123456789abcdef, which is Ethereum's format. Both were plausible when they were typed and nobody had made the call yet.

The map is the idea, and the map is a placeholder

The emissions map was supposed to be the argument: look at where the carbon is, then look at who is paying for it. It is also the clearest illustration of how far the mock-up is from a product. index.js declares an emissionsData object listing "India" twelve times with different values, so all but the last collapse into a single key before the loop ever runs, leaving three markers instead of fourteen. Each of those is dropped at Math.random() latitude and longitude, which puts most of them in open ocean. The marker radius is emissions / 1000, so a 9,000-MtCO2 country renders as a nine-pixel circle.

Meanwhile countries.geojson sits in the same directory with five well-formed Point features carrying real centroids and per-country emissions figures, which is the thing the map should have been reading. It is never fetched. That gap between the correct data file and the loop that ignores it is the whole design pass in miniature.

The one part that was real

Minting an SPL token is not one call. You have to fund a mint account to rent exemption, initialize it, create the associated token account, mint the supply into it, and attach a Metaplex metadata account, or you end up with a token that has no name and no logo anywhere in a wallet. CreateToken.tsx does the whole sequence in a single signature, assembling one Transaction from five instructions and sending it with a freshly generated mint keypair co-signing alongside the connected wallet: a SystemProgram.createAccount sized to MINT_SIZE and funded from getMinimumBalanceForRentExemptMint, a createInitializeMintInstruction taking the decimals from the form with the wallet as both mint and freeze authority, a createAssociatedTokenAccountInstruction for the wallet's ATA, a createMintToInstruction for amount * 10 ** decimals so the form takes a human supply figure and the scaling happens once in one place, and a createCreateMetadataAccountV3Instruction against the PDA derived from ["metadata", token-metadata program id, mint].

The tempting shortcut is to send those five instructions as five transactions, and it is worse than it sounds. Each needs its own signature, so the user clicks through five wallet prompts and can abandon at any of them; and because the mint keypair is generated in the browser, a run that dies after step two leaves a mint account on chain that nobody can find again, funded with the user's lamports and useless. Solana's transaction size limit is what makes bundling viable here - five instructions and two signers fit comfortably inside it - so once the atomic version is available there is no argument for the other one. Either the token exists fully described or it does not exist.

The upload has to come first, for the same reason. The metadata instruction takes a URI and not a JSON blob, so the off-chain half has to be published before the on-chain half can reference it, and somewhere permanent, because a token whose logo is on a server you stop paying for is a token with no logo. That is what the Bundlr path is for: the uploader takes a bundler node, checks it answers by asking for its Solana address, waits on ready(), prices the upload against the file length, compares that with the balance already loaded on the node, and funds from the connected wallet if it is short. Two uploads follow, the image as image/png and then the metadata document as application/json, each coming back as an arweave.net URL. The token itself is created with isMutable: false, the right default for something you intend to hand out, which means the update page only ever works on tokens minted elsewhere. Getting the Solana libraries to run under Next.js 12 needed webpack fallbacks for the Node builtins the SDKs assume. The page titles in that app still read CarbonX, in six files, which is the evidence that these were one effort rather than two.

Where the mock shows through

Being blunt about the design pass: the marketplace page still lists "Shoes" and "Sneakers" priced at $19.99 and $29.99, with a stock photo of a running shoe, from the template it was cut out of. The token form posts nowhere. The dashboard's wallet balance is a literal $5,000 and the transaction list has two of its promised five rows. The profile is John Doe. The marketing site's services section still carries Lorem Ipsum under three headings, and its contact map is a Google Maps instance hard-centred on lower Manhattan, inherited from the template and never moved. The transaction table on the operator console has an HTML comment in it reading "Add 13 more entries with random company names." None of that is hidden, and none of it needed fixing, because the artefact was never meant to run. It was meant to be looked at.

The token layer's seams are all in the off-chain half. The two uploads are independent, so the image URL is not stitched into the metadata JSON automatically - the UI tells you to paste it into the image and uri fields yourself and re-upload, which works but is the obvious next thing to automate. Funding is a flat one SOL whenever the loaded balance falls short of the price, regardless of how much short it actually is. The bundler dropdown offers a mainnet node while the RPC connection is pinned to devnet in the context provider, so choosing mainnet there is a half-configured state rather than a supported one. The uploader's wallet effect is declared without a dependency array, so it re-runs the provider connect on every render. And the create and update paths disagree on mutability: creation writes isMutable: false while the update instruction sets isMutable: true and primarySaleHappened: true unconditionally, so it is written for tokens looser than the ones this app mints. The stack has aged too, at Next 12, React 17 and a node:12-alpine Dockerfile that installs production dependencies and then reinstalls the dev ones to run the build.

Why it stopped

Drawing the transaction and profile screens made it clear how much of the design rested on settlement rules and credit verification nobody had specified, and none of that is a front-end problem. A create-token form with a name, a unit value and a supply is trivial to draw and meaningless without an issuer who is accountable for the tonnes behind it. Minting is the solved part, which is why it is the part that got finished. The marketplace looked fine until the question became what happens between clicking Buy Now and the credit changing hands, at which point the answer was a backend that did not exist.

The right conclusion from a week of HTML is usually that the next step is not more HTML. The idea came back later with an actual backend behind it, as the Green Credit work, where the parcel geometry and the verification evidence got built first and the screens came after.

Project Details
RESOURCES

STATUS
Prototype
YEAR

2023

TYPE

Experiment · Blockchain

TAGS
Web3
Sustainability
Prototype
Solana