2026-10-04 18:14:49 +02:00
import { generateBip39Mnemonic as lo , deriveSeedFromBip39Mnemonic as ze , deriveHdPrivateNodeFromSeed as uo , deriveHdPath as fo , secp256k1 as Ye , hash160 as ho , encodeCashAddress as Ms , lockingBytecodeToCashAddress as po , cashAddressToLockingBytecode as $s , binToHex as go , hexToBin as Hs , utf8ToBin as Je , binToBase64 as oe , base64ToBin as se } from "@bitauth/libauth" ; var vt = { chipnet : { id : "chipnet" , label : "chipnet" , prefix : "bchtest" , beacon : "bchtest:qp8njaa9a3ahg7259s5qajnce526n0rttsq8qwx4jl" , beaconScripthash : "e1cdbba719436f49acb4bf245b44df875c6d6b405eda46a9d3e736d138102f3b" , tldBeacon : "bchtest:qzc4c76vtw9nd49g6x8nmvc0jz62uvk4dc7hmpmerh" , tldBeaconScripthash : "c85db10e106606273422b2733b2a6fb8b35a4d0e7dbfb20cf59bdb0833c008b8" , electrum : [ { url : "wss://silentmode.st/electrum" , ip : "195.184.247.106" } , { url : "wss://coinspectrum.duckdns.org:50011" , ip : "195.184.247.106" } , { url : "wss://chipnet.bch.ninja:50004" , ip : "135.148.236.244" } , { url : "wss://chipnet.imaginary.cash:50004" , ip : "66.29.152.199" } , { url : "wss://tbch4.loping.net:62002" , ip : "183.89.202.207" } ] , beaconOnly : /silentmode\.st\/electrum|coinspectrum\.duckdns\.org:50011/ , electrumListName : "electrum.bch" , rootTldName : "tlds.bch" , bootstrapTlds : [ "bch" ] , startHeight : 2e5 , hostingBucket : "bns" , nameListUrl : "https://dl.silentmode.st/bns-name-snapshot.json" , nameListMirrorUrl : "https://navigate.st/bns/silentmode.bch/bns-name-snapshot.json" , cdnIndexUrl : "https://dl.silentmode.st/bns-name-index.json" , nostrTags : { names : "bns-name-list" , tlds : "bns-tld-list" } , explorerTx : "https://chipnet.imaginary.cash/tx/" , faucets : [ { name : "tbch.googol.cash" , url : "https://tbch.googol.cash/" } , { name : "chipnet.bch.ninja faucet" , url : "https://chipnet.bch.ninja/faucet" } ] , operatorAddress : "bchtest:qzew3dqcsj0guwe5y3uxf9q2xs5ajgy9lca4z093cu" , accountPath : "m/44'/145'/0'" , bchnRpcPort : 48332 , blocklists : [ { name : "sirius-blocklist.x" , beacon : "bchtest:qrrnwvuasq5fy63mdvp3umx5qfavtu7mnq950rdssw" , scripthash : "fa14c5fd7a22bd1856b44168823a1ab3ff50961c917dafbcba749aeaa7b26646" } ] , covenant : { version : 1 , fundLock : "76a914b2e8b418849e8e3b34247864940a3429d92085fe88ac" , steward : "02817a308db60d949f5a0543085f6757578dab169d4381934e997cbaef52c34c0d" , blocksPerYear : 52560 , graceBlocks : 26280 , tiers : [ { maxLabel : 2 , renewalSats : 2e6 , head : "011e0350cd0002a86604809698000400e1f5050380841e" } , { maxLabel : 4 , renewalSats : 5e5 , head : "011e0350cd0002a86603a025260440787d010320a107" } , { maxLabel : 7 , renewalSats : 2e5 , head : "011e0350cd0002a8660340420f048096980003400d03" } , { maxLabel : 255 , renewalSats : 1e5 , head : "011e0350cd0002a8660320a10703404b4c03a08601" } ] , tail : "105279a97b887b7bad51b275827701209c2102817a308db60d949f5a0543085f6757578dab169d4381934e997cbaef52c34c0d1976a914b2e8b418849e8e3b34247864940a3429d92085fe88ac5b79009c635c79a95b79885d7a5d7aad5c7951a269597a815c79597a959376040065cd1d9f6901145a7a7e547e7c54807ec1011a7f777eaa00cd02aa207b7e01877e8800d1c0ce8800d2c0cf8800ccc0c6a26951cd8851cc537a597a95a26951d100886d6d6d7551675b79519c635e7a820114885d79a95c7a885d7a5d7aad01147c7e547e5a7a7ec1011a7f777eaa00cd02aa207b7e01877e8800d1c0ce8800d2c0cf8800ccc0c6a2696d6d6d6d6d51675b79529c635c7a820114885a7a8176b175c576040065cd1d9f697c597993587900709f635a795a795c79945479567994955d7996937b757c785e799501649677686079827701209dc0cf01117a7eaa011456797e01207e7c7e587a7e51c702aa207baa7e01877e8853795c7993040065cd1d9f690114557a7e547e547a5b7a9354807ec1011a7f777eaa00cd02aa207b7e01877e8800d1c0ce8800d2c0cf8800ccc0c6a26951cd547a8851cc7b527994a26951d100887600a06352cd0376a9145a797e0288ac7e8852cc78a26952d10088686d6d6d6d6d51675b79539c635c79a95b7a885c7a5c7aadc0cec454a16900d178879169c451a06351d17887916968c452a06352d17887916968c453a06353d178879169686d6d6d6d6d6d51675b7a549d5b79a95b79885c7a5c7aadc1011a7f77aa5c7a7c5d79aa7eaa537abb01145a7a7e547e597a7e597a7eaa00cd02aa207b7e01877e8800d1c0ce8800d2c0cf8800ccc0c6a2696d6d6d6d5168686868" } } , mainnet : { id : "mainnet" , label : "mainnet" , prefix : "bitcoincash" , beacon : null , beaconScripthash : null , tldBeacon : null , tldBeaconScripthash : null , electrum : [ { url : "wss://bch.imaginary.cash:50004" , ip : "67.223.119.97" } , { url : "wss://electrum.imaginary.cash:50004" , ip : "15.204.113.162" } , { url : "wss://electroncash.dk:50004" , ip : "87.63.200.138" } , { url : "wss://bch.loping.net:50004" , ip : " 183.89 .
feat(market): names can be priced in US dollars — a covenant holds the certificate, a gateway-signed oracle sets the BCH amount
A BCH-priced offer is a signed transaction with a fixed amount, so a
seller who wants "$500" had to relist whenever the rate moved. The new
USD mode moves the certificate into a small CashScript covenant
(contracts/usd-listing.cash). Anyone may buy it by paying the seller
target_cents / price, where the price comes from a message signed by the
gateway's oracle key — the median of five exchanges the site already
uses. The payout is clamped to a floor/ceiling band the seller sets, and
the seller can reclaim the certificate at any time.
What the chain can and cannot enforce, stated plainly in the contract and
the UI: script can require a transaction to be mined no earlier than a
time, never no later, so a quote cannot expire on-chain. The band is the
guarantee, and quotes are bound to the listing's outpoint so cancelling
and relisting voids every quote ever issued. An offline libauth VM test
covers list, buy, underpayment, foreign-listing quotes, forged quotes,
band clamping, cancel and a non-seller cancel; on chipnet aloevera.test
was listed at $0.30, bought by a second wallet for 119,090 sat at
$251.91, listed again by the new owner and cancelled.
Gateway: GET /api/price/oracle[?txid=] serves the oracle public key and
per-listing signed quotes; the key lives in oracle.key beside the
gateway, never in the repo. POST /api/market accepts kind "usd" after
reproducing the redeem script from the stated terms and finding the
certificate in that covenant; such listings are pruned once the covenant
output is spent; DELETE accepts the listing's seller while the index
shows the covenant as holder.
Dashboard: the Sell tab has "Price is fixed in: US dollars / BCH", a
band control with the resulting BCH range, and cancel through the
covenant. Market: USD listings show the dollar price, today's BCH
equivalent and the band; buying fetches a quote and completes the
covenant spend. Studio no longer sends a needless UPD when the registry
view is stale, and missing files on a name now get a real 404 page.
2026-09-20 19:20:46 +02:00
` ))>=0;){let s=this.buf.slice(0,n).trim();if(this.buf=this.buf.slice(n+1),s)try{this._handle(JSON.parse(s))}catch{}}let o=this.buf.trim();if(o)try{let s=JSON.parse(o);this._handle(s),this.buf=""}catch{}}call(e,n=[]){let o=++this.id;return new Promise((s,r)=>{this.pending.set(o,{resolve:s,reject:r}),this.ws.send(JSON.stringify({id:o,method:e,params:n})+ `
2026-10-04 18:14:49 +02:00
` ),setTimeout(()=>{this.pending.has(o)&&(this.pending.delete(o),r(new Error( ` electrum timeout : $ { e } ` )))},2e4)})}close(){try{this.ws.close()}catch{}}},Fn=t=>typeof t=="string"?{url:t}:t;async function Ct(t,e,{directIP:n=!1}={}){let o;for(let s of t){let r=Fn(s);try{return await new Lt(r.url,e).connect()}catch(i){o=i}if(n&&r.ip)try{let i=new URL(r.url),a= ` $ { i . protocol } //${r.ip}:${i.port}`;return await new Lt(a,e,{servername:i.hostname}).connect()}catch(i){o=i}}throw o??new Error("no electrum server reachable")}function Mn(t){if(!t||!t.startsWith("6a"))return null;let e=Ut(t),n=1,o=[];for(;n<e.length;){let r=e[n++],i;if(r<=75)i=r;else if(r===76)i=e[n++];else if(r===77)i=e[n]|e[n+1]<<8,n+=2;else return null;o.push(e.slice(n,n+i)),n+=i}let s=new TextDecoder().decode(o[0]??new Uint8Array);if(!s.startsWith(Pe))return null;try{let r=JSON.parse(s.slice(Pe.length));if(!$n.has(r.o))return null;if(typeof r.n=="string"||r.o==="RPT"&&Array.isArray(r.n)&&r.n.every(i=>typeof i=="string"))return r}catch{}return null}function Qt(t){let e=null;for(let n of t||[]){let o=Mn(n&&n.scriptPubKey?n.scriptPubKey.hex:null);if(o){if(e)return null;e=o}}return e}var $n=new Set(["REG","UPD","REL","TREG","TUPD","RPT","BLK","UBK"]);function Zt(t,e){let n=t.height>0?t.height:1/0,o=e.height>0?e.height:1/0;return n-o||t.tx_hash.localeCompare(e.tx_hash)}function te(t,e){let n=new Set(t.map(a=>a.tx_hash)),o=new Map(t.map(a=>{let l=new Set;for(let c of e.get(a.tx_hash)?.vin??[])c.txid&&c.txid!==a.tx_hash&&n.has(c.txid)&&l.add(c.txid);return[a.tx_hash,l]})),s=new Set,r=[],i=[...t];for(;i.length;){let a=i.findIndex(c=>[...o.get(c.tx_hash)].every(h=>s.has(h)));a<0&&(a=0);let[l]=i.splice(a,1);s.add(l.tx_hash),r.push(l)}return r}function We(t,e){let n=e&&e.tokenData;if(!n||!n.nft||typeof n.category!="string")return!1;let o=n.category;if(!(t.vin||[]).some(i=>i&&i.txid===o&&i.vout===0)||(t.vout||[]).filter(i=>i.tokenData&&i.tokenData.category===o).length!==1)return!1;let s=n.nft.capability;if(s!=null&&s!==""&&s!=="none")return!1;let r;try{r=BigInt(n.amount??0)}catch{return!1}return r===0n}function Yt(t){let e=t.lastIndexOf(".");return e<0?t:t.slice(e+1)}function Hn(t,e,n){let o=n.get(t);return o?o.regHeight>0&&e>0?o.regHeight<=e:!0:!1}async function ee(t,e=S){let{tldBeaconScripthash:n}=Xt(e),o=typeof t.unionHistory=="function"?(await t.unionHistory(n)).history:await t.call("blockchain.scripthash.get_history",[n]);o.sort(Zt);let s=new Map;for(let{tx_hash:i}of o)try{s.set(i,await t.call("blockchain.transaction.get",[i,!0]))}catch{}let r=new Map;for(let{tx_hash:i,height:a}of te(o.filter(l=>s.has(l.tx_hash)),s)){let l=s.get(i),c=l.vout||[],h=Qt(c);if(!h||typeof h.n!="string"||!$e.test(h.n))continue;let d=h.n;if(h.o==="TREG"){if(r.has(d))continue;let u=Jt(d),f=c.find(y=>y.tokenData?.nft&&y.tokenData.nft.commitment===u);if(!f||!We(l,f))continue;r.set(d,{category:f.tokenData.category,regHeight:a,regTxid:i,mintScriptHex:f.scriptPubKey?.hex??null,records:h.r??{},updates:[{height:a,txid:i,records:h.r??{}}]})}else if(h.o==="TUPD"){let u=r.get(d);if(!u||!c.some(f=>f.tokenData&&f.tokenData.category===u.category))continue;u.records=h.r??{},u.updates.push({height:a,txid:i,records:u.records})}}return r}function Kn(t,e){if(!t)return{};let n=t.updates||[];if(!n.length)return t.records||{};let o={};for(let s of n)e>0&&(s.height<=0||s.height>e)||(o=s.records||{});return o}function Wn(t,e){let n=Kn(t,e);return n.policy==="cosign"||n.hidden===1||n.hidden===!0||n.hidden==="1"}function Gn(t,e,n){let o=Jt(n);return(t||[]).some(s=>s.tokenData&&s.tokenData.category===e.category&&s.tokenData.nft&&s.tokenData.nft.commitment===o)}function Ne(t){if(!t||!t.scriptPubKey)return null;let e=t.scriptPubKey;return typeof e.address=="string"?e.address:Array.isArray(e.addresses)&&e.addresses.length?e.addresses[0]:null}function Ge(t,e){let n=t&&t.txid?e.get(t.txid):null;return!n||!Array.isArray(n.vout)?null:n.vout.find(o=>o&&o.n===t.vout)??n.vout[t.vout]??null}function jn(t,e,n){return!t||!e||!e.category||(t.vout||[]).some(o=>o.tokenData&&o.tokenData.category===e.category)?!1:(t.vin||[]).some(o=>{let s=Ge(
feat(market): names can be priced in US dollars — a covenant holds the certificate, a gateway-signed oracle sets the BCH amount
A BCH-priced offer is a signed transaction with a fixed amount, so a
seller who wants "$500" had to relist whenever the rate moved. The new
USD mode moves the certificate into a small CashScript covenant
(contracts/usd-listing.cash). Anyone may buy it by paying the seller
target_cents / price, where the price comes from a message signed by the
gateway's oracle key — the median of five exchanges the site already
uses. The payout is clamped to a floor/ceiling band the seller sets, and
the seller can reclaim the certificate at any time.
What the chain can and cannot enforce, stated plainly in the contract and
the UI: script can require a transaction to be mined no earlier than a
time, never no later, so a quote cannot expire on-chain. The band is the
guarantee, and quotes are bound to the listing's outpoint so cancelling
and relisting voids every quote ever issued. An offline libauth VM test
covers list, buy, underpayment, foreign-listing quotes, forged quotes,
band clamping, cancel and a non-seller cancel; on chipnet aloevera.test
was listed at $0.30, bought by a second wallet for 119,090 sat at
$251.91, listed again by the new owner and cancelled.
Gateway: GET /api/price/oracle[?txid=] serves the oracle public key and
per-listing signed quotes; the key lives in oracle.key beside the
gateway, never in the repo. POST /api/market accepts kind "usd" after
reproducing the redeem script from the stated terms and finding the
certificate in that covenant; such listings are pruned once the covenant
output is spent; DELETE accepts the listing's seller while the index
shows the covenant as holder.
Dashboard: the Sell tab has "Price is fixed in: US dollars / BCH", a
band control with the resulting BCH range, and cancel through the
covenant. Market: USD listings show the dollar price, today's BCH
equivalent and the band; buying fetches a quote and completes the
covenant spend. Studio no longer sends a needless UPD when the registry
view is stale, and missing files on a name now get a real 404 page.
2026-09-20 19:20:46 +02:00
` ))>=0;){let s=this.buf.slice(0,n).trim();if(this.buf=this.buf.slice(n+1),s)try{this._handle(JSON.parse(s))}catch{}}let o=this.buf.trim();if(o)try{let s=JSON.parse(o);this._handle(s),this.buf=""}catch{}}call(e,n=[]){let o=++this.id;return new Promise((s,r)=>{this.pending.set(o,{resolve:s,reject:r}),this.ws.send(JSON.stringify({id:o,method:e,params:n})+ `
2026-10-04 18:14:49 +02:00
` ),setTimeout(()=>{this.pending.has(o)&&(this.pending.delete(o),r(new Error( ` electrum timeout : $ { e } ` )))},2e4)})}close(){try{this.ws.close()}catch{}}};async function Ze(t=Mt,e){let n=[];for(let o of t)try{return await new ce(o,e).connect()}catch(s){n.push( ` $ { o } : $ { s ? . message ? ? s } ` )}throw wo(t.length,n)}function wo(t,e){let n=new Error( ` no electrum server reachable ( tried $ { t } ) . These use non - standard ports , which some networks and VPNs block :
bns: one network table instead of chipnet constants copied into fifteen places
Every client hard-coded the chipnet beacons, address prefix and electrum
servers on its own: resolver, registrar config, wallets, gateway, indexer,
mirror scripts, the browser bundle and the mobile Java. A mainnet launch would
have meant finding all of them and hoping none was missed.
The table now lives in resolver-web.js, the one file every client already
shares, so it stays a single-file drop-in. BNS_NETWORK selects the record;
unset means chipnet, so nothing changes today: the live index resolves the
same 60 names and 20 TLDs, the 67 offline tests pass, and the dashboard,
market and studio load the same values through BNS.NETWORK.
The mainnet record carries the verified public servers, the prefix and its
own Sia bucket, but its beacons, start height and operator address are
deliberately null: requireBeacons() refuses to scan until they are pinned in
the order ROADMAP-MAINNET.md §6 requires. Bns.java reads a generated
BnsNetwork.java so the phone cannot drift from the desktop clients.
NETWORK-CONFIG.md records what reads the table and what a launch still pins.
2026-09-27 21:18:37 +02:00
$ { e . join ( `
2026-10-04 18:14:49 +02:00
` )} ` ) ; return n . code = "NO_ELECTRUM" , n . failures = e , n } import { cashAddressToLockingBytecode as bo , lockingBytecodeToCashAddress as ko , sha256 as So , hash160 as Ys , binToHex as To , hexToBin as Vs } from "@bitauth/libauth" ; function tn ( t ) { return To ( So . hash ( t ) . slice ( ) . reverse ( ) ) } function C ( t ) { let e = bo ( t ) ; if ( typeof e == "string" ) throw new Error ( ` bad address " ${ t } ": ${ e } ` ) ; return e . bytecode } function vo ( t ) { return tn ( C ( t ) ) } function j ( t ) { let e = C ( t ) , n = String ( t ) . includes ( ":" ) ? t . split ( ":" ) [ 0 ] : S . prefix , o = ko ( { bytecode : e , prefix : n , tokenSupport : ! 0 } ) ; if ( typeof o == "string" ) throw new Error ( ` cannot token-encode " ${ t } ": ${ o } ` ) ; return o . address } async function en ( t , e ) { let n = C ( e ) , o = tn ( n ) ; return ( await t . call ( "blockchain.scripthash.listunspent" , [ o , "include_tokens" ] ) ) . map ( r => ( { txid : r . tx _hash , vout : r . tx _pos , satoshis : BigInt ( r . value ) , height : r . height , address : e , lockingBytecode : n , token : r . token _data ? { category : r . token _data . category , amount : BigInt ( r . token _data . amount ? ? 0 ) , nft : r . token _data . nft ? { capability : r . token _data . nft . capability , commitment : r . token _data . nft . commitment } : void 0 } : void 0 } ) ) } async function q ( t , e ) { let n = [ ] ; for ( let o of e ) n . push ( ... await en ( t , o ) ) ; return n } async function tt ( t , e ) { return t . call ( "blockchain.transaction.broadcast" , [ e ] ) } async function nn ( t , e , n = ! 0 ) { return t . call ( "blockchain.transaction.get" , [ e , n ] ) } import { binToHex as Pt , hexToBin as V , utf8ToBin as yt , binToUtf8 as de , encodeTransaction as Eo , decodeTransaction as Zs } from "@bitauth/libauth" ; var Oo = "BNS1" , lt = S . beacon , fe = S . tldBeacon , I = 600 n , R = 1000 n , K = 546 n , le = "bch" , ue = 200 , _o = /^[a-z0-9-]{1,32}$/ , on = /^[a-z0-9-]{1,16}$/ ; function Et ( t ) { let e = String ( t ) . toLowerCase ( ) . trim ( ) . replace ( /^\.+/ , "" ) . replace ( /\.+$/ , "" ) ; if ( ! on . test ( e ) ) throw new Error ( ` invalid TLD " ${ t } " (allowed: [a-z0-9-]{1,16}) ` ) ; return e } var $t = class extends Error { constructor ( e ) { super ( e ) , this . name = "NoGenesisInputError" , this . code = "NO_GENESIS_INPUT" } } , X = class extends Error { constructor ( e , { needSats : n , haveSats : o } = { } ) { super ( e ) , this . name = "InsufficientFundsError" , this . code = "INSUFFICIENT_FUNDS" , this . needSats = n , this . haveSats = o } } ; function D ( t ) { let e = String ( t ) . toLowerCase ( ) . trim ( ) . replace ( /\.$/ , "" ) . split ( "." ) . filter ( Boolean ) , n , o ; if ( e . length === 0 ) throw new Error ( ` invalid name " ${ t } " ` ) ; if ( e . length === 1 ? ( n = e [ 0 ] , o = le ) : ( o = e [ e . length - 1 ] , n = e [ e . length - 2 ] ) , ! _o . test ( n ) || ! on . test ( o ) ) throw new Error ( ` invalid name " ${ t } " (allowed: <label>.<tld>, [a-z0-9-]) ` ) ; return o === le ? n : ` ${ n } . ${ o } ` } function z ( t ) { return t . includes ( "." ) ? t : ` ${ t } . ${ le } ` } function J ( t , e , n , o = null ) { let s = JSON . stringify ( o ? { o : t , n : e , r : n ? ? { } , c : o } : { o : t , n : e , r : n ? ? { } } ) , r = yt ( Oo + s ) ; if ( r . length > ue ) throw new Error ( ` records too large ( ${ r . length } > ${ ue } bytes) ` ) ; return r } function he ( t , e = "REG" ) { return ue - J ( e , t , { } ) . length } function Ao ( t ) { return t . length <= 75 ? Uint8Array . from ( [ t . length , ... t ] ) : t . length <= 255 ? Uint8Array . from ( [ 76 , t . length , ... t ] ) : Uint8Array . from ( [ 77 , t . length & 255 , t . length >> 8 & 255 , ... t ] ) } function et ( t ) { return Uint8Array . from ( [ 106 , ... Ao ( t ) ] ) } function pe ( ) { return Uint8Array . from ( [ 65 , ... new Uint8Array ( 65 ) , 33 , ... new Uint8Array ( 33 ) ] ) } var Bo = t => ! t . token ; function F ( t , e , { requireGenesis : n = ! 0 } = { } ) { let o = t . filter ( Bo ) , s = [ ] , r = 0 n ; if ( n ) { let a = o . filter ( l => l . vout === 0 ) . sort ( ( l , c ) => c . satoshis > l . satoshis ? 1 : - 1 ) ; if ( a . length === 0 ) throw new $t ( "wallet has no coin at output index 0, which CashTokens requires to create a new certificate category \u2014 send a small amount to yourself first (buildPrepTx)" ) ; s . push ( a [ 0 ] ) , r += a [ 0 ] . satoshis } let i = o . filter ( a => ! s . some ( l => l . txid === a . txid && l . vout === a . vout ) ) . sort ( ( a , l ) => l . satoshis > a . satoshis ? 1 : - 1 ) ; for ( let a of i ) { if ( r >= e ) break ; s . push ( a ) , r += a . satoshis } return { inputs : s , totalSats : r } } function mt ( t ) { return { outpointTransactionHash : V ( t . txid ) , outpointIndex : t . vout , sequenceNumber : 4294967294 , unlockingBytecode : pe ( ) } } function bt ( t ) { return { ... mt ( t ) , lockingBytecode : t . lockingBytecode , valueSatoshis : t . satoshis , ... t . token ? { token : { amount : t . token . amount ? ? 0 n , category : V ( t . token . category ) , ... t . token . nft ? { nft : { capability : t . token . nft . capability , commitment : V ( t . token . nft . commitment || "" ) } } : { } } } : { } } } function rt ( t ) { return Eo ( t ) . length } function ge ( { name : t , ownerAddress : e , ownerTokenAddress : n , changeAddress : o , records : s = { } , utxos : r , feeAddress : i = null , feeSat