2222 .primary { background : var (--brand ); color : # 1a0f08 ; padding : 13px 18px ; width : 100% ; margin-top : 18px ; }
2323 .primary : disabled { opacity : .5 ; cursor : not-allowed; }
2424 .ghost { background : transparent; color : var (--ink ); border : 1px solid var (--line ); padding : 9px 14px ; }
25+ .ghost : disabled { opacity : .5 ; cursor : not-allowed; }
2526 .row { display : flex; align-items : center; justify-content : space-between; gap : 12px ; }
2627 .pill { font-family : ui-monospace, monospace; font-size : 12px ; color : var (--muted ); }
28+ .triple { display : grid; gap : 6px ; }
29+ .triple .hint { font-size : 11px ; color : var (--muted ); margin-top : 8px ; }
30+ .triple .hint a { color : var (--brand ); }
2731 .fee { margin-top : 14px ; padding : 12px 14px ; border : 1px solid var (--line ); border-radius : 10px ; font-size : 13px ; }
2832 .fee b { color : var (--brand ); }
2933 # log { margin-top : 18px ; font-family : ui-monospace, monospace; font-size : 12px ; color : var (--muted );
3539 < body >
3640 < div class ="card ">
3741 < div class ="kicker "> Useless dApp</ div >
38- < h1 > Do a pointless thing 🪄 </ h1 >
39- < p class ="sub "> … and route a fee to an affiliate through the Intuition FeeProxy. This is all the integration takes.</ p >
42+ < h1 > Make a pointless statement </ h1 >
43+ < p class ="sub "> Create a triple from three existing atoms, and route a fee to an affiliate through the Intuition FeeProxy. No IPFS pinning needed — triples only reference atoms that already exist . This is all the integration takes.</ p >
4044
4145 < div class ="row ">
4246 < span class ="pill " id ="acct "> Not connected</ span >
@@ -46,20 +50,31 @@ <h1>Do a pointless thing 🪄</h1>
4650 < label > Affiliate address (the one you registered)</ label >
4751 < input id ="affiliate " value ="0xE596096F4176b682E300d73963e7B04B383C1AA1 " />
4852
53+ < div class ="triple ">
54+ < div class ="row ">
55+ < label style ="margin:14px 0 0 "> Triple — three existing atom IDs (subject · predicate · object)</ label >
56+ < button class ="ghost " id ="load " style ="margin-top:14px "> Load 3 from chain</ button >
57+ </ div >
58+ < input id ="subject " placeholder ="subject atom ID (0x… 32 bytes) " />
59+ < input id ="predicate " placeholder ="predicate atom ID (0x… 32 bytes) " />
60+ < input id ="object " placeholder ="object atom ID (0x… 32 bytes) " />
61+ < div class ="hint "> These must be atom IDs that already exist on Intuition testnet. Use “Load 3 from chain”, or paste IDs you find on the < a href ="https://portal.intuition.systems/explore/atom " target ="_blank " rel ="noopener "> portal explorer</ a > .</ div >
62+ </ div >
63+
4964 < label > Amount (TRUST)</ label >
5065 < input id ="gross " value ="0.5 " />
5166
5267 < div class ="fee " id ="fee "> Fee preview: connect + fill above…</ div >
5368
54- < button class ="primary " id ="go " disabled > Create an atom via this affiliate</ button >
69+ < button class ="primary " id ="go " disabled > Create a triple via this affiliate</ button >
5570
5671 < div id ="log "> </ div >
5772 </ div >
5873
5974 < script type ="module ">
6075 import {
6176 createPublicClient , createWalletClient , custom , http ,
62- parseAbi , parseEther , formatEther , toHex , isAddress ,
77+ parseAbi , parseEther , formatEther , isAddress , isHex ,
6378 } from 'https://esm.sh/viem@2'
6479
6580 const FEEPROXY = '0x15458457aC9009BB83137025E5D228A0783207d7'
@@ -74,13 +89,14 @@ <h1>Do a pointless thing 🪄</h1>
7489 }
7590
7691 const feeProxyAbi = parseAbi ( [
77- 'function createAtomsVia (address affiliate, bytes [] atomDatas , uint256[] assets, (uint256 maxFeeBps, uint256 maxFixedFee) feeGuard) payable returns (bytes32[])' ,
92+ 'function createTriplesVia (address affiliate, bytes32 [] subjectIds, bytes32[] predicateIds, bytes32[] objectIds , uint256[] assets, (uint256 maxFeeBps, uint256 maxFixedFee) feeGuard) payable returns (bytes32[])' ,
7893 'function previewCreationFee(address affiliate, uint256 grossAssets) view returns (uint256 fee, uint256 forwarded)' ,
7994 'function isAffiliateActive(address affiliate) view returns (bool)' ,
8095 ] )
8196 const multiVaultAbi = parseAbi ( [
8297 'function isApprovedToCreate(address sender, address creator) view returns (bool)' ,
8398 'function approve(address sender, uint8 approvalType)' ,
99+ 'event AtomCreated(address indexed creator, bytes32 indexed termId, bytes atomData, address atomWallet)' ,
84100 ] )
85101
86102 const publicClient = createPublicClient ( { chain, transport : http ( ) } )
@@ -89,12 +105,12 @@ <h1>Do a pointless thing 🪄</h1>
89105 const $ = ( id ) => document . getElementById ( id )
90106 const log = ( msg , cls = '' ) => { $ ( 'log' ) . insertAdjacentHTML ( 'beforeend' , `<div class="${ cls } ">${ msg } </div>` ) ; $ ( 'log' ) . scrollTop = 1e9 }
91107 const short = ( a ) => a . slice ( 0 , 6 ) + '…' + a . slice ( - 4 )
108+ const isId = ( v ) => isHex ( v ) && v . length === 66
92109
93110 async function connect ( ) {
94111 if ( ! window . ethereum ) return log ( 'No wallet found (install MetaMask).' , 'err' )
95112 walletClient = createWalletClient ( { chain, transport : custom ( window . ethereum ) } )
96113 ; [ account ] = await walletClient . requestAddresses ( )
97- // ensure correct chain
98114 try { await walletClient . switchChain ( { id : chain . id } ) }
99115 catch { try { await walletClient . addChain ( { chain } ) } catch { } }
100116 $ ( 'acct' ) . textContent = short ( account ) + ' · Intuition testnet'
@@ -103,49 +119,76 @@ <h1>Do a pointless thing 🪄</h1>
103119 refreshFee ( )
104120 }
105121
122+ // Read recent AtomCreated logs straight from the chain — no indexer — and
123+ // prefill the three fields with distinct existing atom IDs. A triple over
124+ // arbitrary recent atoms is meaningless on purpose (it is a useless dApp),
125+ // but every component is a real, already-pinned atom, so it always works.
126+ async function loadAtoms ( ) {
127+ $ ( 'load' ) . disabled = true
128+ try {
129+ log ( 'Reading recent AtomCreated events…' )
130+ const latest = await publicClient . getBlockNumber ( )
131+ const fromBlock = latest > 200000n ? latest - 200000n : 0n
132+ const logs = await publicClient . getLogs ( {
133+ address : MULTIVAULT ,
134+ event : multiVaultAbi . find ( ( x ) => x . type === 'event' && x . name === 'AtomCreated' ) ,
135+ fromBlock, toBlock : 'latest' ,
136+ } )
137+ const ids = [ ...new Set ( logs . map ( ( l ) => l . args . termId ) ) ] . slice ( - 3 )
138+ if ( ids . length < 3 ) return log ( 'Not enough recent atoms found — paste IDs from the portal instead.' , 'err' )
139+ $ ( 'subject' ) . value = ids [ 0 ] ; $ ( 'predicate' ) . value = ids [ 1 ] ; $ ( 'object' ) . value = ids [ 2 ]
140+ log ( `Loaded ${ short ( ids [ 0 ] ) } · ${ short ( ids [ 1 ] ) } · ${ short ( ids [ 2 ] ) } ` , 'ok' )
141+ } catch ( e ) {
142+ log ( 'Could not read atoms: ' + ( e . shortMessage || e . message ) + ' — paste IDs from the portal instead.' , 'err' )
143+ } finally { $ ( 'load' ) . disabled = false }
144+ }
145+
106146 async function refreshFee ( ) {
107147 const affiliate = $ ( 'affiliate' ) . value . trim ( )
108148 if ( ! isAddress ( affiliate ) ) return $ ( 'fee' ) . textContent = 'Enter a valid affiliate address.'
109149 let gross ; try { gross = parseEther ( $ ( 'gross' ) . value . trim ( ) || '0' ) } catch { return }
110150 try {
111151 const active = await publicClient . readContract ( { address : FEEPROXY , abi : feeProxyAbi , functionName : 'isAffiliateActive' , args : [ affiliate ] } )
112- if ( ! active ) return $ ( 'fee' ) . innerHTML = '⚠️ This address is not a registered/active affiliate.'
152+ if ( ! active ) return $ ( 'fee' ) . textContent = 'This address is not a registered/active affiliate.'
113153 const [ fee , forwarded ] = await publicClient . readContract ( { address : FEEPROXY , abi : feeProxyAbi , functionName : 'previewCreationFee' , args : [ affiliate , gross ] } )
114154 $ ( 'fee' ) . innerHTML = `On ${ formatEther ( gross ) } TRUST → affiliate fee <b>${ formatEther ( fee ) } TRUST</b>, forwarded ${ formatEther ( forwarded ) } TRUST.`
115155 } catch ( e ) { $ ( 'fee' ) . textContent = 'Could not read fee: ' + ( e . shortMessage || e . message ) }
116156 }
117157
118158 async function go ( ) {
119159 const affiliate = $ ( 'affiliate' ) . value . trim ( )
160+ const subject = $ ( 'subject' ) . value . trim ( ) , predicate = $ ( 'predicate' ) . value . trim ( ) , object = $ ( 'object' ) . value . trim ( )
120161 let gross ; try { gross = parseEther ( $ ( 'gross' ) . value . trim ( ) ) } catch { return log ( 'Bad amount' , 'err' ) }
121162 if ( ! isAddress ( affiliate ) ) return log ( 'Bad affiliate address' , 'err' )
163+ if ( ! [ subject , predicate , object ] . every ( isId ) ) return log ( 'Each atom ID must be a 32-byte hex value (0x + 64 chars).' , 'err' )
122164 $ ( 'go' ) . disabled = true
123165 try {
124- // 1) one-time MultiVault approval so the FeeProxy can create on your behalf
166+ // 1) one-time MultiVault approval so the FeeProxy can create on your
167+ // behalf — CREATION approval covers both atoms and triples.
125168 const approved = await publicClient . readContract ( { address : MULTIVAULT , abi : multiVaultAbi , functionName : 'isApprovedToCreate' , args : [ FEEPROXY , account ] } )
126169 if ( ! approved ) {
127170 log ( 'Approving FeeProxy for CREATION on the MultiVault…' )
128171 const h = await walletClient . writeContract ( { account, address : MULTIVAULT , abi : multiVaultAbi , functionName : 'approve' , args : [ FEEPROXY , 4 ] } )
129172 await publicClient . waitForTransactionReceipt ( { hash : h } )
130173 log ( `approved · <a href="${ EXPLORER } /tx/${ h } " target="_blank">tx</a>` , 'ok' )
131- } else log ( 'Already approved ✓ ' )
174+ } else log ( 'Already approved' )
132175
133- // 2) route the creation through the affiliate
134- log ( 'Routing createAtomsVia through ' + short ( affiliate ) + '…' )
135- const data = toHex ( `useless-dapp-atom-${ Date . now ( ) } ` )
176+ // 2) route the triple creation through the affiliate
177+ log ( 'Routing createTriplesVia through ' + short ( affiliate ) + '…' )
136178 const guard = { maxFeeBps : 10000n , maxFixedFee : parseEther ( '1' ) }
137179 const hash = await walletClient . writeContract ( {
138- account, address : FEEPROXY , abi : feeProxyAbi , functionName : 'createAtomsVia ' ,
139- args : [ affiliate , [ data ] , [ gross ] , guard ] , value : gross ,
180+ account, address : FEEPROXY , abi : feeProxyAbi , functionName : 'createTriplesVia ' ,
181+ args : [ affiliate , [ subject ] , [ predicate ] , [ object ] , [ gross ] , guard ] , value : gross ,
140182 } )
141183 await publicClient . waitForTransactionReceipt ( { hash } )
142- log ( `🎉 done — fee routed to the affiliate · <a href="${ EXPLORER } /tx/${ hash } " target="_blank">tx</a>` , 'ok' )
184+ log ( `done — triple created, fee routed to the affiliate · <a href="${ EXPLORER } /tx/${ hash } " target="_blank">tx</a>` , 'ok' )
143185 } catch ( e ) {
144186 log ( 'Failed: ' + ( e . shortMessage || e . message ) , 'err' )
145187 } finally { $ ( 'go' ) . disabled = false }
146188 }
147189
148190 $ ( 'connect' ) . onclick = connect
191+ $ ( 'load' ) . onclick = loadAtoms
149192 $ ( 'go' ) . onclick = go
150193 $ ( 'affiliate' ) . oninput = refreshFee
151194 $ ( 'gross' ) . oninput = refreshFee
0 commit comments