From 38eccc8b9d66f331d833216ba15f355c61a90220 Mon Sep 17 00:00:00 2001 From: Richard Moore Date: Thu, 4 Feb 2021 18:54:10 -0500 Subject: [PATCH] docs: more descriptions for logs and events. --- docs.wrm/api/providers/provider.wrm | 87 +++++++++++++++++++++++----- docs.wrm/api/providers/types.wrm | 50 +++++++++------- docs.wrm/concepts/events.wrm | 90 +++++++++++++++++++++++------ 3 files changed, 173 insertions(+), 54 deletions(-) diff --git a/docs.wrm/api/providers/provider.wrm b/docs.wrm/api/providers/provider.wrm index fc728100a..e5665a72a 100644 --- a/docs.wrm/api/providers/provider.wrm +++ b/docs.wrm/api/providers/provider.wrm @@ -2,7 +2,6 @@ _section: Provider @ Explain what a provider is... - _subsection: Accounts Methods @ _property: provider.getBalance(address [ , blockTag = latest ]) => Promise<[[BigNumber]]> @ @SRC @@ -73,6 +72,10 @@ reading and debugging much simpler. The provider offers some basic operations to help resolve and work with ENS names. +_property: provider.getResolver(name) => Promise<[[EnsResolver]]> +Returns an EnsResolver instance which can be used to further inquire +about specific entries for an ENS name. + _property: provider.lookupAddress(address) => Promise @ @SRC Performs a reverse lookup of the //address// in ENS using the //Reverse Registrar//. If the name does not exist, or the @@ -94,6 +97,25 @@ provider.resolveName("ricmoo.firefly.eth"); //! +_subsection: EnsResolver @ + +_property: resolver.name => string +The name of this resolver. + +_property: resolver.address => string<[[address]]> +The address of the Resolver. + +_property: resolver.getAddress([ cointType = 60 ]) => Promise +Returns a Promise which resolves to the [[link-eip-2304]] multicoin address +stored for the //coinType//. By default an Ethereum [[address]] +(``coinType = 60``) will be returned. + +_property: resolver.getContentHash() => Promise +Returns a Promise which resolves to any stored [[link-eip-1577]] content hash. + +_property: resolver.getText(key) => Promise +Returns a Promise which resolves to any stored [[link-eip-634]] text entry for //key//. + _subsection: Logs Methods @ _property: provider.getLogs(filter) => Promise> @ @SRC @@ -115,6 +137,13 @@ Returns the block number (or height) of the most recently mined block. _property: provider.getGasPrice() => Promise<[[BigNumber]]> @ @SRC Returns a //best guess// of the [[gas-price]] to use in a transaction. +_property: provider.ready => Promise<[[providers-Network]]> @ @src +Returns a Promise which will stall until the network has heen established, +ignoring errors due to the target node not being active yet. + +This can be used for testing or attaching scripts to wait until the node is +up and running smoothly. + _code: Network Status Examples @lang // The network information @@ -156,6 +185,21 @@ An estimate may not be accurate since there could be another transaction on the network that was not accounted for, but after being mined affected relevant state. +_property: provider.getTransaction(hash) => Promise<[[providers-TransactionResponse]]> @ @src +Returns the transaction with //hash// or null if the transaction is unknown. + +If a transaction has not been mined, this method will search the transaction +pool. Various backends may have more restrictive transaction pool access (e.g. +if the gas price is too low or the transaction was only recently sent and not +yet indexed) in which case this method may also return null. + +_property: provider.getTransactionReceipt(hash) => Promise<[[providers-TransactionReceipt]]> @ @src +Returns the transaction receipt for //hash// or null if the transaction +has not been mined. + +To stall until the transaction has been mined, consider the ``waitForTransaction`` +method below. + _property: provider.sendTransaction(transaction) => Promise<[[providers-TransactionResponse]]> @ @SRC Submits //transaction// to the network to be mined. The //transaction// **must** be signed, and be valid (i.e. the nonce is correct and the account has sufficient balance to pay @@ -164,36 +208,47 @@ for the transaction). _property: provider.waitForTransaction(hash [ , confirms = 1 [ , timeout ] ]) => Promise<[TxReceipt](providers-TransactionReceipt)> @ @SRC Returns a Promise which will not resolve until //transactionHash// is mined. +If //confirms// is 0, this method is non-blocking and if the +transaction has not been mined returns null. Otherwise, +this method will block until the transaction has //confirms// +blocks mined on top of the block in which is was mined. _subsection: Event Emitter Methods @ -Explain events here... +The EventEmitter API allows applications to use an +[[link-wiki-observer-pattern]] to register callbacks for when +various events occur. + +This closely follows the Event Emitter provided by other JavaScript +libraries with the exception that event names support some more +[complex objects](Provider--events), not only strings. The objects +are normalized internally. _property: provider.on(eventName, listener) => this @ @SRC -Add a //listener// to be triggered for each //eventName//. +Add a //listener// to be triggered for each //eventName// [event](Provider--events). _property: provider.once(eventName, listener) => this @ @SRC -Add a //listener// to be triggered for only the next //eventName//, -at which time it will be removed. +Add a //listener// to be triggered for only the next +//eventName// [event](Provider--events), at which time it will be removed. _property: provider.emit(eventName, ...args) => boolean @ @SRC -Notify all listeners of //eventName//, passing //args// to each listener. This -is generally only used internally. +Notify all listeners of the //eventName// [event](Provider--events), +passing //args// to each listener. This is generally only used internally. _property: provider.off(eventName [ , listener ]) => this @ @SRC -Remove a //listener// for //eventName//. If no //listener// is provided, -all listeners for //eventName// are removed. +Remove a //listener// for the //eventName// [event](Provider--events). +If no //listener// is provided, all listeners for //eventName// are removed. _property: provider.removeAllListeners([ eventName ]) => this @ @SRC -Remove all the listeners for //eventName//. If no //eventName// is provided, -**all** events are removed. +Remove all the listeners for the //eventName// [events](Provider--events). +If no //eventName// is provided, **all** events are removed. _property: provider.listenerCount([ eventName ]) => number @ @SRC -Returns the number of listeners for //eventName//. If no //eventName// is -provided, the total number of listeners is returned. +Returns the number of listeners for the //eventName// [events](Provider--events). +If no //eventName// is provided, the total number of listeners is returned. _property: provider.listeners(eventName) => Array @ @SRC -Returns the list of Listeners for //eventName//. +Returns the list of Listeners for the //eventName// [events](Provider--events). _heading: Events @ @@ -207,7 +262,7 @@ properties ``address`` (the source contract) and ``topics`` (a topic-set to matc If ``address`` is unspecified, the filter matches any contract address. -See events for more information on how to specify topic-sets. +See [EventFilters](providers-EventFilter) for more information on filtering events. _definition: **Topic-Set Filter** @@ -216,6 +271,8 @@ The value of a **Topic-Set Filter** is a array of Topic-Sets. This event is identical to a //Log Filter// with the address omitted (i.e. from any contract). +See [EventFilters](providers-EventFilter) for more information on filtering events. + _definition: **Transaction Filter** The value of a **Transaction Filter** is any transaction hash. diff --git a/docs.wrm/api/providers/types.wrm b/docs.wrm/api/providers/types.wrm index d42a5ab51..29ad5d137 100644 --- a/docs.wrm/api/providers/types.wrm +++ b/docs.wrm/api/providers/types.wrm @@ -1,23 +1,15 @@ _section: Types _subsection: BlockTag @ -A **BlockTag** specifies a specific location in the Blockchain. +A **BlockTag** specifies a specific block location in the Blockchain. -- **``"latest"``** -- The most recently mined block -- **``"earliest"``** -- Block #0 -- **``"pending"``** -- The block currently being prepared for mining; not all +- **``"latest"``** - The most recently mined block +- **``"earliest"``** - Block #0 +- **``"pending"``** - The block currently being prepared for mining; not all operations and backends support this BlockTag -- **//number//** -- The block at this height -- **//a negative number//** -- The block this many blocks ago - -_heading: EventType @ - -And **EventType** can be any of the following. - -- **//string//** -- TODO... -- **//Array> | Array>>>//** -- TODO... -- **//[[providers-EventFilter]]//** -- TODO... - +- **//number//** - The block at this height +- **//a negative number//** - The block this many blocks ago +- **//hex string//** - The block at this height (as a hexidecimal value) _subsection: Networkish @ A **Networkish** may be any of the following: @@ -111,10 +103,15 @@ _heading: EventFilter @ _property: filter.address => string<[[address]]> The address to filter by, or ``null`` to match any address. -_property: filter.topics => Array> | Array>>> -The topics to filter by, or ``null`` to match any topics. Each entry represents an -**AND** condition that must match, or may be ``null`` to match anything. If a given -entry is an Array, then that entry is treated as an **OR** for any value in the entry. +_property: filter.topics => Array> | Array>>> +The topics to filter by or ``null`` to match any topics. + +Each entry represents an **AND** condition that must match, or may +be ``null`` to match anything. If a given entry is an Array, then +that entry is treated as an **OR** for any value in the entry. + +See [Filters](events--filters) for more details and examples +on specifying complex filters. _heading: Filter @ @INHERIT<[[providers-EventFilter]]> @@ -231,9 +228,18 @@ transaction was mined. _property: transaction.raw => string<[[DataHexString]]> The serialized transaction. -_property: transaction.wait([ confirmations = 1 ]) => Promise<[[providers-TransactionReceipt]]> -Wait for //confirmations//. If 0, and the transaction has not been mined, -``null`` is returned. +_property: transaction.wait([ confirms = 1 ]) => Promise<[[providers-TransactionReceipt]]> +Resolves to the [[providers-TransactionReceipt]] once the transaction +has been included in the chain for //confirms// blocks. If //confirms// +is 0, and the transaction has not been mined, ``null`` is returned. + +If the transaction execution failed (i.e. the receipt status is ``0``), +a [CALL_EXCEPTION](errors--call-exception) Error will be rejected with +the following properties: + +- ``error.transaction`` - the original transaction +- ``error.transactionHash`` - the hash of the transaction +- ``error.receipt`` - the actual receipt, with the status of ``0`` _heading: TransactionReceipt @ diff --git a/docs.wrm/concepts/events.wrm b/docs.wrm/concepts/events.wrm index e9493497e..82698605c 100644 --- a/docs.wrm/concepts/events.wrm +++ b/docs.wrm/concepts/events.wrm @@ -1,34 +1,40 @@ -_section: Events - -Explain how topics and such work - -_subsection: Solidity Topics - -How to compute the topic... +_section: Events @ _subsection: Logs and Filtering -Example hog logs are used. +Logs and filtering are used quite often in blockchain applications, +since they allow for efficient queries of indexed data and provide +lower-cost data storage when the data is not required to be +accessed on-chain. -Link to provider.getLogs and contract.on +These can be used in conjunction with the [Provider Events API](Provider--event-methods) +and with the [Contract Events API](Contract--events). -_heading: Filters +The Contract Events API also provides [higher-level methods](Contract--filters) +to compute and query this data, which should be preferred over the lower-level filter. -Filter are used as a way to query ... efficient, explain bloom filters lightly +_heading: Filters @ -A filter may have up to 4 topic-sets, where each topic-set refers -to a condition that must match the log topic in that position (i.e. each -condition is ``AND``-ed together). +When a Contract creates a log, it can include up to 4 pieces of +data to be indexed by. The indexed data is hashed and included in +a [[link-wiki-bloomfilter]], which is a data structure that allows +for efficient filtering. -If a topic-set is ``null``, a log topic in that position is not filtered +So, a filter may correspondingly have up to 4 topic-sets, where each +topic-set refers to a condition that must match the indexed log topic +in that position (i.e. each condition is ``AND``-ed together). + +If a topic-set is ``null``, a log topic in that position is **not filtered** at all and **any value** matches. -If a topic-set is a single topic, a log topic in that position must match +If a topic-set is a single topic, a log topic in that position **must** match **that topic**. If a topic-set is an array of topics, a log topic in that position must -match any **one** of the topics (i.e. the topic in this position are ``OR``-ed). +match **any one** of the topics (i.e. the topic in this position are ``OR``-ed). +This may sound complicated at first, but is more easily understood with +some examples. _table: Example Log Matching @style @@ -152,6 +158,56 @@ contract.filters.Transfer(myAddress, otherAddress) contract.filters.Transfer(null, [ myAddress, otherAddress ]) //! + +_subsection: Solidity Topics @ + +This is a quick (and non-comprehensive) overview of how events are computed +in Solidity. + +This is likely out of the scope for most developers, but may be intersting +to those who want to learn a bit more about the underlying technology. + +Solidity provides two types of events, anonymous and non-anonymous. The +default is non-anonymous, and most developers will not need to worry about +anonymous events. + +For non-anonymous events, up to 3 topics may be indexed (instead of 4), since +the first topic is reserved to specify the event signature. This allows +non-anonymous events to always be filtered by their event signature. + +This topic hash is always in the first slot of the indexed data, and is +computed by normalizing the Event signature and taking the keccak256 hash +of it. + +For anonymous events, up to 4 topics may be indexed, and there is no +signature topic hash, so the events cannot be filtered by the event +signature. + +Each additional indexed property is processed depending on whether its +length is fixed or dynamic. + +For fixed length types (e.g. ``uint``, ``bytes5``), all of which are +internally exactly 32 bytes (shorter types are padded with zeros; +numeric values are padded on the left, data values padded on the right), +these are included directly by their actual value, 32 bytes of data. + +For dynamic types (e.g. ``string``, ``uint256[]``) , the value is hashed +using keccak256 and this hash is used. + +Because dynamic types are hashed, there are important consequences in +parsing events that should be kept in mind. Mainly that the original +value is lost in the event. So, it is possible to tell is a topic is +equal to a given string, but if they do not match, there is no way +to determine what the value was. + +If a developer requires that a string value is required to be both +able to be filtered and also able to be read, the value must be included +in the signature twice, once indexed and once non-indexed (e.g. +``someEvent(string indexed searchBy, string clearText)``). + +For a more detailed description, please refer to the +[Solidity Event Documentation](link-solidity-events). + _heading: Other Things? TODO Explain what happens to strings and bytes, how to filter and retain the value