Repository navigation
v17.3.0
v17.3.0: Protocol 30
Added
- CAP-84 muxed contract (
W…) addresses.StrKeyaddsencodeMuxedContract(),decodeMuxedContract()andisValidMuxedContract().Addressadds themuxedContracttype andAddress.muxedContract(). The newencodeMuxedContractAddress()builds aW…address from aC…address and an ID.extractBaseAddress()also accepts aC…address, which it returns unchanged, and aW…address, for which it returns the baseC…address.TransactionBuilder.addSacTransferOperation()accepts aW…destination (#1811). - Horizon
contract_creditedeffects (ContractCredited) have optionalcontract_muxedandcontract_muxed_idfields. Horizon sets them when a Stellar Asset Contracttransferormintsends to a muxed contract (W…) address.contract_muxed_idis a decimal string (#1836, stellar-horizon#237). - The XDR types are regenerated from stellar-xdr with CAP-84, CAP-87 and CAP-88 enabled.
xdr.ScAddressadds thescAddressTypeMuxedContractarm andxdr.MuxedContract.xdr.ContractCostTypeadds the ML-DSA cost types, frommlDsa44DecodeVerifyingKeytoverifyMlDsa87Sig.xdr.StellarValueTypeaddsstellarValueSignedMsandstellarValueEmptyTxSetMs, withxdr.StellarValueSignedMsValueandxdr.StellarValueProposedMsValue. The newSC_SPEC_TYPE_NAME_LIMITconstant (1024) raises the limit on contract spec type names from 60. Code that switches over these unions with an exhaustive check must handle the new arms (#1795, #1796, #1797).
Changed
inSuccessfulContractCallonrpc.Api.EventResponseandrpc.Api.RawEventResponseis optional and deprecated, because newer RPC servers no longer return it fromgetEvents(#1836, stellar-rpc#1050). With such a server it isundefined. Code that uses it as abooleancan fail to compile understrict. The field will be removed in the next major release.- The
rpc.Serverconstructor throws whentimeoutis not0or a whole number of milliseconds up to 2147483647. Examples areNaN, a negative number or a fraction. Before, it accepted any value and ignored it. The CLI's--timeoutflag has the same upper limit (#1802). StellarToml.Resolver.resolve()rejects before the request whenallowedRedirectsis not 0 or a whole number up toNumber.MAX_SAFE_INTEGER. Before, it passed the value to the HTTP client. In Node, the Fetch build followed redirects without limit forNaN, and both builds did so forInfinity(#1809).TransactionBuilder.setTimeout()now throws "timeout ends before min_time" when a positive timeout ends before an existingminTime. Before, it built a transaction withminTimeaftermaxTime, which is never valid (#1781).- When you set
maxRedirectsormaxContentLength, the Fetch client rejects a successful response whose body is not valid UTF-8, with "Response body is not valid UTF-8". Before, it replaced the invalid bytes with U+FFFD and returned the result. The client still ignores thecharsetofContent-Type, because a stellar.toml file and a federation response must be UTF-8. A non-2xx response still rejects with its HTTP error (#1804). Federation.Server.createForDomain()andFederation.Server.resolve()reject aFEDERATION_SERVERURL fromstellar.tomlthat has a user name or password, or a scheme other thanhttp:orhttps:, even whenallowHttpis set. The SDK does not check the host, so a URL can point to a private or local address, such ashttps://10.0.0.5. A server-side application that resolves untrusted addresses must filter the hosts it connects to. WhenallowedRedirectsis set, redirect targets are not checked. TheFederation.Serverconstructor does not check the URL that you pass to it (#1809).- Contract clients reject a muxed (
M…orW…) address for anAddressparameter; pass it to aMuxedAddressparameter instead.authorizeEntry(),authorizeInvocation()andbuildWithDelegatesEntry()reject a muxed signer or delegate address.rpc.Server.getContractData()rejects a muxed owner and a key that contains a muxed address.rpc.Server.getAssetBalance()andgetSACBalance()reject a muxed address, since balances are held by the base address. Before, each of these built a request that the network rejects or can never match (#1811, #1835). nativeToScVal()andSpec.nativeToScVal()throw aTypeErrorfor a function value. Before, they called the function and converted its result. To keep the old result, call the function and pass its return value.Specalso treats a struct field named like an inheritedObject.prototypefunction as missing when the value does not set it. Examples aretoStringandconstructor. Before, contract clients encoded that field from the inherited function, for exampletoStringas"[object Undefined]"(#1834).
Fixed
toJson()on aMuxedEd25519Account, or on anScAddressthat holds one, throws when the ID is outside the uint64 range. Before, it wrapped the ID, so-1ngave anM…address with ID 2^64 - 1 and2n ** 64ngave one with ID 0.toXdr()already threw for these IDs (#1811).CallBuilder.stream()passes an event that it cannot parse toonerrorand keeps the stream open.onerrorgets aMessageEventof type"error"whosedatais the parse error. The stream moves its cursor to the event's SSEid, so a reconnect does not get the event again. Anonerrorhandler that closes the stream now also closes it on such an event. Before, the event listener threw the error as an uncaught exception, which stops a Node.js process that has no handler for it (#1806).- Horizon records no longer get a link method whose name is
then,toJSON,paging_token,_linksor the name of anObject.prototypeproperty, such as__proto__orconstructor. The link stays in_links. Before, athenlink madecall()hang while it sent requests in a loop. AtoJSONlink madeJSON.stringify()return{}and send a request. A__proto__link changed the record's prototype. Apaging_tokenlink madestream()reconnect with the link function's source code as its cursor. A_linkslink replaced_linkswith a function, so each link method after it threw (#1806). scValToNative()throws aTypeErrorwhen two keys of anscvMapconvert to the same object property, such asu32 1andsymbol "1". It previously kept only the last entry. Maps whose keys all convert to distinct properties decode as before. Because any contract can emit such a map,humanizeEvents()and contract-client decoding ofVal-typed values can now throw on data read from the network (#1778).- Templated Horizon
_linksfunctions now resolve a relativehrefthe same way non-templated links do. They previously threwTypeError: Invalid URL(#1771). MuxedAccount,encodeMuxedAccount()andKeypair.xdrMuxedAccount()accept an ID only as decimal digits.MuxedAccountreturns the ID in canonical form fromid(). Before, any string thatBigInt()accepts passed, such as"0x10"," 16 "or"".id()returned that string as typed, so two objects for the same M-address could report different IDs, and an empty string silently became ID 0. An ID string longer than 22 characters is now rejected before it is parsed, so a very long string no longer costs CPU time and memory (#1780).Operation.manageSellOffer()andOperation.manageBuyOffer()acceptofferIdonly as a non-negative safe integer, a non-negative bigint, or a string of decimal digits, up to the int64 maximum.Operation.revokeOfferSponsorship()applies the same rule, and now also accepts a number or a bigintofferId, such as theofferIdof a decodedLedgerKeyOffer. Before, a number above 2^53 - 1 was rounded and could name a different offer. An empty string meant offer 0, and a negative ID built an operation that the network rejects. AnofferIdstring longer than 22 characters is now rejected before it is parsed (#1780).TransactionBuilder.setTimeout(TimeoutInfinite)keeps an existingminTime. Before, it reset both time bounds to 0, so a transaction meant to become valid later was valid at once (#1781).new Asset(code, issuer)keeps an issued asset's code as given. It previously changed every spelling ofxlmtoXLM, even when an issuer was present. As a result,Asset.fromOperation()andtransaction.operationsshowedXLM:G…for an operation onxlm:G…. Encoding the decoded asset again also changed its bytes. An asset built asnew Asset("xlm", issuer)now encodesxlm, so its contract ID changes. To keep the old asset, passXLM(#1784).TransactionandFeeBumpTransactionnow decode their own copy of aTransactionEnvelopeobject passed to the constructor, toTransactionBuilder.fromXdr()or fromTransactionBuilder.build(). Before, they kept the caller's object. A later change to that object altered whathash(),sign()andtoXdr()used, but not always whatoperationsshowed. An envelope that cannot be encoded, such as one with more than 100 operations, now fails in the constructor and inTransactionBuilder.build(), not later intoXdr()orsign()(#1784).WebAuth.readChallengeTx(),verifyChallengeTxSigners()andverifyChallengeTxThreshold()throw anInvalidChallengeErrorwhen a challenge'sclient_domainoperation has no source account. SEP-10 requires that source to be the client domain's signing key. The verifiers previously skipped the client domain signature check for such a challenge. (#1788)WebAuth.buildChallengeTx()throws whenclientSigningKeyis the server's public key.WebAuth.readChallengeTx(),verifyChallengeTxSigners()andverifyChallengeTxThreshold()throwInvalidChallengeErrorwhen theclient_domainoperation's source account is the server account. The server signature previously also counted as the client domain signature, andverifyChallengeTxSigners()could leave a valid client signer out of its result. (#1791)- In Node, when
maxRedirectsis set, the Fetch client rejects a redirect to a URL that is nothttp:orhttps:. It also rejects a redirect fromhttps:tohttp:. Before, it followed both. For example,StellarToml.Resolver.resolve()withallowedRedirectsreturned the content of adata:URL. When a redirect changes the method to GET, the client now also removes theContent-Encoding,Content-Language,Content-LocationandContent-Rangeheaders. (#1799) rpc.Servernow applies itstimeoutoption to each request. Before, the option was ignored, so a request to a server that did not answer stayed pending. This also fixes the--timeoutflag of the bindings CLI, which had no effect (#1802).- The Fetch client sends
paramswhen the URL has a#fragment. Before, it added them after the fragment, so the server did not receive them. When there areparams, the fragment is now left out of the request URL, as in the Axios build (#1804). - In Node, when
maxRedirectsis set, the Fetch client keeps only a safe set of headers on a redirect to another origin:Accept,Accept-Encoding,Accept-Language,Content-Type,Content-Length,Content-Encoding,Content-Language,Content-Location,Content-Range,User-Agent,Range,Cache-Control,Pragma, theIf-*conditional headers,X-Client-Name,X-Client-Version,X-App-NameandX-App-Version. Other custom headers, such astraceparent, are dropped too. Anhttp:tohttps:redirect on the same host counts as another origin. Before, the client removed onlyAuthorization,Proxy-AuthorizationandCookie. Other credential headers, such as the HorizonauthToken(X-Auth-Token) or an API key inheaders, reached the new origin. A redirect to the same origin keeps all headers (#1814). AssembledTransaction.sign()andsignAndSend()keep the fee from simulation. Before, they added the Soroban resource fee to the fee a second time, which raised the inclusion fee bid. The automatic footprint restore now uses thefeeoption (orBASE_FEE) as the inclusion fee. Before, it used the restore's resource fee as the inclusion fee and ignoredfee. Setfeeto bid more during surge pricing.restoreFootprint()still acceptsminResourceFeebut does not use it (#1808).- Contract client
MuxedAddressparameters accept anAddressobject, as theirstring | Addresstype says. Before, they threw "was not address, but value was Address" (#1811). TransactionBuilder.build()throws for a Soroban transaction that has a muxedM…source account, on the transaction or on an operation, or that has a memo.addSacTransferOperation()throws as soon as it is called on a builder with a muxed source. The network rejects all of these, so the transaction could never succeed (#1811, #1835).- In Node, and in the Fetch build in a browser,
Federation.Serverno longer follows redirects. Before, it followed up to 21 redirects in the Axios build and up to 20 in the Fetch build, although the v15.1.0 entry for #1390 listed a redirect guard for it. The Axios build in a browser, and both builds in React Native, still follow redirects, because XHR follows them itself. To follow redirects, set the newallowedRedirectsoption, as forStellarToml.Resolver.resolve().createForDomain()andresolve()also apply it to thestellar.tomlrequest. The constructor,createForDomain()andresolve()throw whenallowedRedirectsis not 0 or a whole number up toNumber.MAX_SAFE_INTEGER, such asNaN,Infinity,-1,1.5or2 ** 54(#1809). Spec.parseEvent()matches amapevent declaration only when the event data is anscvMap, and avecdeclaration only when the data is anscvVec. Otherwise it tries the next declaration. Before, it treated other data as an empty map or vec. Amapdeclaration then matched without its data params, so a single-value event declared later never matched. For example, a mint event withi128data was returned as the map-formatMintwithout itsamount, in place ofMintWithAmountOnly. The generated bindings' event parsing method callsSpec.parseEvent(), so it has the same fix (#1817).TransactionBuilder.build(),TransactionBuilder.cloneFrom(),TransactionBuilder.buildFeeBumpTransaction()andrpc.assembleTransaction()throw when a SorobanresourceFeeis below 0 or above 2^50. Before, a negative value raised the fee incloneFrom()andassembleTransaction(), andbuild()made a transaction that the network rejects. When theresourceFeeequals the fee,cloneFrom()andassembleTransaction()now use an inclusion fee of 0. Before, they counted the resource fee twice, soAssembledTransaction.sign()withfee: "0"signed 2 × the resource fee (#1832).authorizeEntry()replaces the other signatures of aSOROBAN_CREDENTIALS_ADDRESS_WITH_DELEGATESentry withscvVoidwhenvalidUntilLedgerSeqdiffers from a non-zero stored expiration. The top-level account and all delegates sign one payload that includes the expiration. Before, the call changed the stored expiration and kept the other signatures, which then did not verify.checkAuthEntryReadiness()now lists those signers inunsignedBy. When the stored expiration is 0, as simulation returns,authorizeEntry()keeps an ed25519 signature on another node only when it verifies atvalidUntilLedgerSeq. It keeps a custom signature as it is.AssembledTransaction.signAuthEntries()keeps the stored expiration of aSOROBAN_CREDENTIALS_ADDRESS_WITH_DELEGATESentry by default while it lies within the next 100 ledgers. So delegates that signed first keep their signatures. Every other entry gets the latest ledger + 100, as before. An explicitexpirationstill applies to every entry (#1830).- The docs of the generated
.valuegetter onstring<N>union arms (xdr.MemomemoText, andxdr.ScValscvString,scvSymbolandscvExecutableTag) now say that it decodes leniently: bytes that are not valid UTF-8 become U+FFFD, so a value rebuilt from.valuecan have different bytes. The arm field (.text,.str,.sym,.executableTag) keeps the bytes. The docs ofXdrStringandMemo.text()now say that a lone surrogate in a JS string encodes as U+FFFD with no error. TheXdrStringheader comment also said thattoString()renders the SEP-0051 escape form. That form comes fromtoJson()(#1824). - The SDK throws "Expected at most one 'contractspecv0' section in wasm, found " when a Wasm has more than one
contractspecv0custom section. Empty sections count. This applies tocontract.Spec.fromWasm(), tocontract.Client.fromWasm(),fromWasmHash(),from()anddeploy(), toBindingGenerator.fromWasm(),fromWasmHash()andfromContractId(), and to the bindings CLI. Before, the SDK used the first non-empty section and ignored the others. Other tools can read a different section, so they could show a different contract interface. SEP-48 stores the interface in one section. A section name with a leading byte-order mark no longer counts ascontractspecv0, which matches wasmparser-based tools (#1827).
Full Changelog: v17.2.1...v17.3.0