From 720b188be84415eba0f3cffb0258354ea38eca6c Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Mon, 10 Aug 2020 07:39:51 -0500 Subject: [PATCH 1/6] Initial draft of EIP-0012 Technical spec for wallet-dApp communication. --- eip-0012.md | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) create mode 100644 eip-0012.md diff --git a/eip-0012.md b/eip-0012.md new file mode 100644 index 00000000..55aee6ca --- /dev/null +++ b/eip-0012.md @@ -0,0 +1,277 @@ +# EIP-0012: dApp-Wallet Web Bridge + +* Authors: rooooooooob, Robert Kornacki +* Created: 10-Aug-2020 +* License: CC0 +* Forking: not needed + +## Description + +This document describes a communication bridge between cryptocurrency wallets and Ergo dApps which interact via javascript code injected into a web context. The communication is one-way from the dApp to the wallet. + +## Motivation + +Distributed apps (dApps) often require access to a user's wallet in order to function, and this is typically from a web context. In other cryptocurrencies, such as Ethereum, this is done via a library such as web3 that injects code into a web browser that the dApp can interact with to create transactions or obtain address data using a user's wallet. This should be done in a manner that maximizes privacy and an easy user experience. + +## API + +The proposed API is limited to just the minimum wallet <-> dApp communication needed rather than providing lots of utility functions (tx building, data conversions, etc) or node-access (for UTXO scanning). This is different compared to web3 as that functionality could be modular in a separate library and Ergo smart contracts don't need as much direct node access for basic dApp functionality compared to Ethereum. + +This API is accessibly from a javascript object `ergo` that is injected into the web context. This object contains just the `request_read_access()` method upon dApp start. + +### request_read_access() -> Promise\ + +If the API was not already injected, requests access to the API, and if the user consents, injects the full API similar to EIP-1102 into the `ergo` object. Returns true if the API has been injected, or false if the user refused access. +Does nothing and returns true if the API was already injected. + +The wallet can choose to maintain its own whitelist if desired but this is on the wallet side and not a part of this standard. It could possibly also expose a subset of its state as responses to the other calls but this is also out of scope. All following API methods are injected upon consent. + +### check_read_access() -> bool + +Returns true if the full API was injected and we have read access, and false otherwise. This is potentially useful to have a cleaner UX on the dApp side (e.g. display request read access button for optional functionality rather than always requesting it). + +### get_utxos(amount: Value = undefined, token_id: String = 'ERG', paginate: Paginate = 'undefined') -> [Box] | undefined | PaginateError + +Returns a list of all unspent boxes tracked by the wallet that it is capable of signing for. +Takes an optional {amount} parameter, if not `undefined`, limits the returned UTXOs to {amount} of the token {token_id} to create it. if {amount} is not `undefined` and there is not sufficient balance of the specified token to reach {amount} then `undefined` is returned. +If {paginate} is not `undefined` then results are returned in a paginated manner in chronological order of use on the blockchain. If {paginate} is out of range, `PaginateError` is returned. + +### get_balance(token_id: String = 'ERG') -> Value + +Returns available balance of {token_id} owned by the wallet. A useful and minimal convenience functionality, that while could be served by iterating the return of `get_utxos` and summing, is useful enough and could be most likely quickly returned by wallets without needing to do this calculation. + +### get_used_addresses(paginate: Paginate = 'undefined') -> [Address] + +Returns all used (exist in confirmed transactions) addresses of the user's wallet. +If {paginate} is not `undefined` then results are returned in a paginated manner in chronological order of use on the blockchain. If {paginate} is out of range, `PaginateError` is returned. + +### get_unused_addresses() -> [Address] + +Returns unused addresses available that have not been used yet. The wallet would need to pre-generate addresses here within its restrictions (e.g. discover gap on indices for HD wallets). These can be used for receive addresses. + +### sign_tx(tx: Tx) -> Promise\ | TxSignError + +A signed tx is returned if the wallet can provide proofs for all inputs (P2PK ones). If not it produces an error. This should also have some kind of user pop-up which if rejected would produce an error as well. This way signing/keys are kept entirely in the wallet. + +### sign_tx_input(tx: Tx, index: number) -> Promise\ | TxSignError + +Lower level tx signing functionality that signs a single input if the wallet is able to, and returns an error if not. This can be useful for constructing transactions consisting of inputs from multiple parties, and should be general enough for future transaction signing use-cases. Likewise with `sign_tx` this should ask for user consent and can be rejected producing an error. + +### sign_data(addr: Address, message: String) -> Promise\ | DataSignError + +Signs the generic data {data} encoded as a hex string using the private key associated with {addr} if possible. If the wallet does not own this address, an error is returned. This should ask for user consent and produce an error if rejected. The wallet should also implement a message signing protocol such as the proposed [EmIP-005](https://github.com/Emurgo/EmIPs/blob/5b00fce84f31eb763892186eb9c88739ec809333/specs/emip-005.md) in order to make this endpoint safer for use, and make it harder for the user to accidentally sign data such as transactions, blocks, etc. Returns the signed data as a hex-encoded string. + +TBD: Hash algorithm/format to use. + +### submit_tx(tx: SignedTx) -> TxId | TxSendError + +Uses the wallet to send the given tx and returns the TxId for the dApp to track when/if it is included as its own responsibility. This is technically not mandatory but any wallet that is using such a bridge should have this functionality. Wallets can additionally choose to rate-limit this call, and can return an error if they did not or could not send the transaction. + +### add_external_box(box_id: BoxId) + +Informs the wallet of a box it should track, specifically for P2S/P2SH boxes that the wallet might not be aware of. The wallet does not need to follow this suggestion and can handle how it does this on its own. + +## Data Formats + +Most of these types are meant to be the same as the JSON types in the Ergo full node, which matches up as of now with the `sigma-rust` representations. + +### Address + +String as the standard base58 encoding of an address. + +### Box + +Same as `sigma-rust`: +``` +{ + boxId: BoxId, + value: Value, + ergoTree: ErgoTree, + assets: [TokenAmount], + additionalRegisters: { String => Constant }, + creationHeight: int, + transactionId: TxId, + index: int, +} +``` +Additional registers have string ids "R4", "R5" to "R9". + +### BoxCandidate + +Same as `Box` without `transactionId` and `index` as those will be computed when the tx is completed. + +### BoxId + +Hex string of the box id. + +### Constant + +Uses `sigma-rust` rep - Hex-encoded bytes for `SigmaSerialize` of a constant. + +### ContextExtension + +Uses the full node JSON rep (id to hex-encoded binary buffer) +``` +{ + values: { String => String } +} +``` +or the empty `{}` object for P2PK inputs. + +### ErgoTree + +Uses `sigma-rust` rep - Hex string representing serialized bytes of the tree using `SigmaSerialize` + +### Input + +Read-only input (e.g. oracle usage or one yet to be signed) that is not spendable. Uses `sigma-rust` JSON rep: +``` +{ + boxId: BoxId, +} +``` +This is `DataInput` in `sigma-rust` and was renamed since it's used for both data inputs and for unsigned inputs. +For an `Input` in `sigma-rust`, see `SignedInput` here. + +### SignedInput + +Uses `sigma-rust` rep: +``` +{ + boxId: BoxId, + spendingProof: ProverResult, +} +``` +This is `Input` in `sigma-rust`. + +## Paginate + +``` +{ + page: int, + limit: int, +} +``` +Used to specify optional pagination for some API calls. Limits results to {limit} each page, and uses a 0-indexing {page} to refer to which of those pages of {limit} items each. + +## PaginateError + +``` +{ + maxSize: int, +} +``` +{maxSize} is the maximum size for pagination and if we try to request pages outside of this boundary this error is thrown. + +### ProverResult +Uses `sigma-rust` rep - proof is a byte array serialized using `SigmaSerialize` in hex format: +``` +{ + proofBytes: String, + extension: ContextExtension, +} +``` + +### SignedTx + +Uses `sigma-rust` rep for a transaction: + +``` +{ + id: TxId, + inputs: [SignedInput], + dataInputs: [Input], + outputs: [Box], + size: int, +} +``` +with the difference with `Tx` is that the inputs are signed (`Input` instead of `DataInput`) + + +### TokenAmount + +Uses `sigma-rust` rep: +``` +{ + tokenId: TokenId, + amount: Value, +} +``` + +### TokenId + +Uses `sigma-rust` rep - Hex string of the id. + +### Tx + +An unsigned transaction. Uses a modified version of the transaction representation in `sigma-rust`. +``` +{ + id: TxId, + inputs: [Input], + dataInputs: [Input], + outputCandidates: [BoxCandidate], +} +``` +This differs from `Tx` in that that `inputs` would be `[Input]`, however we haven't signed this tx yet and also the outputs are merely candidates. + +### TxId + +Same as `sigma-rust` - string hex encoding + +### TxSendError + +``` +TxSendErrorCode { + Refused: 1, + Failure: 2, +} +TxSendError { + code: TxSendErrorCode, + info: String +} +``` + +* Refused - Wallet refuses to send the tx (could be rate limiting) +* Failure - Wallet could not send the tx + +### TxSignError + +``` +TxSignErrorCode { + ProofGeneration: 1, + UserDeclined: 2, +} +TxSignError { + code: TxSignErrorCode, + info: String +} +``` + +* ProofGeneration - User has accepted the transaction sign, but the wallet was unable to sign the transaction (e.g. not having some of the private keys) +* UserDeclined - User declined to sign the transaction + + +### DataSignError + +``` +DataSignErrorCode { + ProofGeneration: 1, + AddressNotPK: 2, + UserDeclined: 3, + InvalidFormat: 4, +} +DataSignError { + code: DataSignErrorCode, + info: String +} +``` + +* ProofGeneration - Wallet could not sign the data (e.g. does not have the secret key associated with the address) +* AddressNotPK - Address was not a P2PK address and thus had no SK associated with it. +* UserDeclined - User declined to sign the data +* InvalidFormat - If a wallet enforces data format requirements, this error signifies that the data did not conform to valid formats. + +### Value + +BigNum-like object. Represents a value which may or may not be ERG. Value is in the smallest possible unit (e.g. nanoErg for ERG). It can be either a `number` or a `string` using standard unsigned integer text representation. From c745a55a0568d9d0af70d2ac70cdded5cc3b1173 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Wed, 12 Aug 2020 16:41:03 -0500 Subject: [PATCH 2/6] responding to comments --- eip-0012.md | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/eip-0012.md b/eip-0012.md index 55aee6ca..f259d670 100644 --- a/eip-0012.md +++ b/eip-0012.md @@ -2,6 +2,7 @@ * Authors: rooooooooob, Robert Kornacki * Created: 10-Aug-2020 +* Modified: 12-Aug-2020 * License: CC0 * Forking: not needed @@ -11,13 +12,13 @@ This document describes a communication bridge between cryptocurrency wallets an ## Motivation -Distributed apps (dApps) often require access to a user's wallet in order to function, and this is typically from a web context. In other cryptocurrencies, such as Ethereum, this is done via a library such as web3 that injects code into a web browser that the dApp can interact with to create transactions or obtain address data using a user's wallet. This should be done in a manner that maximizes privacy and an easy user experience. +Distributed apps (dApps) often require access to a user's wallet in order to function, and this is typically from a web context. In other cryptocurrencies, such as Ethereum, this is done via a library such as web3 that injects code into a web browser that the dApp can interact with to create transactions or obtain address data using a user's wallet. This should be done in a manner that maximizes privacy and provides an easy user experience. ## API The proposed API is limited to just the minimum wallet <-> dApp communication needed rather than providing lots of utility functions (tx building, data conversions, etc) or node-access (for UTXO scanning). This is different compared to web3 as that functionality could be modular in a separate library and Ergo smart contracts don't need as much direct node access for basic dApp functionality compared to Ethereum. -This API is accessibly from a javascript object `ergo` that is injected into the web context. This object contains just the `request_read_access()` method upon dApp start. +This API is accessible from a javascript object `ergo` that is injected into the web context. This object contains just the `request_read_access()` method upon dApp start. ### request_read_access() -> Promise\ @@ -67,9 +68,10 @@ TBD: Hash algorithm/format to use. Uses the wallet to send the given tx and returns the TxId for the dApp to track when/if it is included as its own responsibility. This is technically not mandatory but any wallet that is using such a bridge should have this functionality. Wallets can additionally choose to rate-limit this call, and can return an error if they did not or could not send the transaction. -### add_external_box(box_id: BoxId) +### add_external_box(box_id: BoxId) -> bool Informs the wallet of a box it should track, specifically for P2S/P2SH boxes that the wallet might not be aware of. The wallet does not need to follow this suggestion and can handle how it does this on its own. +Returns true if the box is now tracked (regardless of whether it was tracked before), and false if it is not tracked. ## Data Formats @@ -81,7 +83,7 @@ String as the standard base58 encoding of an address. ### Box -Same as `sigma-rust`: +Transaction output box. Format is the same as `sigma-rust`: ``` { boxId: BoxId, @@ -110,7 +112,7 @@ Uses `sigma-rust` rep - Hex-encoded bytes for `SigmaSerialize` of a constant. ### ContextExtension -Uses the full node JSON rep (id to hex-encoded binary buffer) +Uses the full node JSON rep (id to hex-encoded Sigma-state value) ``` { values: { String => String } @@ -122,27 +124,35 @@ or the empty `{}` object for P2PK inputs. Uses `sigma-rust` rep - Hex string representing serialized bytes of the tree using `SigmaSerialize` -### Input +### DataInput -Read-only input (e.g. oracle usage or one yet to be signed) that is not spendable. Uses `sigma-rust` JSON rep: +Read-only input (e.g. oracle usage) that is not spendable. Uses `sigma-rust` JSON rep: ``` { boxId: BoxId, } ``` -This is `DataInput` in `sigma-rust` and was renamed since it's used for both data inputs and for unsigned inputs. -For an `Input` in `sigma-rust`, see `SignedInput` here. ### SignedInput -Uses `sigma-rust` rep: +A signed `UnsignedInput`. Uses full-node JSON rep: ``` { boxId: BoxId, spendingProof: ProverResult, + extension: ContextExtension, +} +``` + +### UnsignedInput + +Uses full-node JSON rep: +``` +{ + boxId: BoxId, + extension: ContextExtension, } ``` -This is `Input` in `sigma-rust`. ## Paginate @@ -180,12 +190,12 @@ Uses `sigma-rust` rep for a transaction: { id: TxId, inputs: [SignedInput], - dataInputs: [Input], + dataInputs: [DataInput], outputs: [Box], size: int, } ``` -with the difference with `Tx` is that the inputs are signed (`Input` instead of `DataInput`) +with the difference with `Tx` is that the inputs are signed (`SignedInput` instead of `UnsignedInput`) ### TokenAmount @@ -208,12 +218,12 @@ An unsigned transaction. Uses a modified version of the transaction representati ``` { id: TxId, - inputs: [Input], - dataInputs: [Input], + inputs: [UnsignedInput], + dataInputs: [DataInput], outputCandidates: [BoxCandidate], } ``` -This differs from `Tx` in that that `inputs` would be `[Input]`, however we haven't signed this tx yet and also the outputs are merely candidates. +This differs from `Tx` in that that `inputs` would be `[UnsignedInput]`, however we haven't signed this tx yet and also the outputs are merely candidates. ### TxId From a47bce047a986e30cb7521787fd6cd275f29a85b Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Fri, 12 Mar 2021 03:40:01 -0800 Subject: [PATCH 3/6] Update EIP-0012 based on minor changes thought of during initial implementation of it in Yoroi. Type definitions are specified using flow types now instead of some pseudo JS schema. API errors are added to all API calls to handle things that can go wrong for any call,e.g. internal error in the wallet, incorrect params specified to API calls. get_change_address() was added as an explicit API since it should be easy to support and allows for an easier way during tx creation rather than arbitrarily choosing an address from the used or unused API calls, which aren't garuanteed to be non-empty. add_external_box was removed as we are still unsure how any wallets would be able to implement it in any meaningful way. This could be reversed and have most wallets treat it as a no-op. --- eip-0012.md | 209 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 123 insertions(+), 86 deletions(-) diff --git a/eip-0012.md b/eip-0012.md index f259d670..ed3060c4 100644 --- a/eip-0012.md +++ b/eip-0012.md @@ -2,13 +2,13 @@ * Authors: rooooooooob, Robert Kornacki * Created: 10-Aug-2020 -* Modified: 12-Aug-2020 +* Modified: 12-Mar-2021 * License: CC0 * Forking: not needed ## Description -This document describes a communication bridge between cryptocurrency wallets and Ergo dApps which interact via javascript code injected into a web context. The communication is one-way from the dApp to the wallet. +This document describes a communication bridge between cryptocurrency wallets and Ergo dApps which interact via javascript code injected into a web context. The communication is initiated from the dApp to the wallet. ## Motivation @@ -18,64 +18,88 @@ Distributed apps (dApps) often require access to a user's wallet in order to fun The proposed API is limited to just the minimum wallet <-> dApp communication needed rather than providing lots of utility functions (tx building, data conversions, etc) or node-access (for UTXO scanning). This is different compared to web3 as that functionality could be modular in a separate library and Ergo smart contracts don't need as much direct node access for basic dApp functionality compared to Ethereum. -This API is accessible from a javascript object `ergo` that is injected into the web context. This object contains just the `request_read_access()` method upon dApp start. +This API is accessible from a javascript object `ergo` that is injected into the web context upon wallet consent. Before this just the free `ergo_request_read_access()` and `ergo_check_read_access()` functions are injected into the web context. An event handler can also be registered to the web context to detect wallet disconnects tagged as `"ergo_wallet_disconnected"`. -### request_read_access() -> Promise\ +The following definitions are provided using flow type annotations to describe the underlying javascript API / JSON data types. -If the API was not already injected, requests access to the API, and if the user consents, injects the full API similar to EIP-1102 into the `ergo` object. Returns true if the API has been injected, or false if the user refused access. +### ergo_request_read_access(): Promise\ + +Errors: APIError + +If the API was not already injected, requests access to the API, and if the user consents, injects the full API similar to EIP-1102 via an `ergo` object. Returns true if the API has been injected, or false if the user refused access. Does nothing and returns true if the API was already injected. The wallet can choose to maintain its own whitelist if desired but this is on the wallet side and not a part of this standard. It could possibly also expose a subset of its state as responses to the other calls but this is also out of scope. All following API methods are injected upon consent. -### check_read_access() -> bool +### ergo_check_read_access(): Promise\ + +Errors: APIError Returns true if the full API was injected and we have read access, and false otherwise. This is potentially useful to have a cleaner UX on the dApp side (e.g. display request read access button for optional functionality rather than always requesting it). -### get_utxos(amount: Value = undefined, token_id: String = 'ERG', paginate: Paginate = 'undefined') -> [Box] | undefined | PaginateError +### ergo.get_utxos(amount: Value = undefined, token_id: String = 'ERG', paginate: Paginate = 'undefined'): Promise\ + +Errors: APIError, PaginateError Returns a list of all unspent boxes tracked by the wallet that it is capable of signing for. Takes an optional {amount} parameter, if not `undefined`, limits the returned UTXOs to {amount} of the token {token_id} to create it. if {amount} is not `undefined` and there is not sufficient balance of the specified token to reach {amount} then `undefined` is returned. If {paginate} is not `undefined` then results are returned in a paginated manner in chronological order of use on the blockchain. If {paginate} is out of range, `PaginateError` is returned. -### get_balance(token_id: String = 'ERG') -> Value +### ergo.get_balance(token_id: String = 'ERG'): Promise\ + +Errors: APIError Returns available balance of {token_id} owned by the wallet. A useful and minimal convenience functionality, that while could be served by iterating the return of `get_utxos` and summing, is useful enough and could be most likely quickly returned by wallets without needing to do this calculation. -### get_used_addresses(paginate: Paginate = 'undefined') -> [Address] +### ergo.get_used_addresses(paginate: Paginate = 'undefined'): Promise\ + +Errors: APIError, PaginateError Returns all used (exist in confirmed transactions) addresses of the user's wallet. If {paginate} is not `undefined` then results are returned in a paginated manner in chronological order of use on the blockchain. If {paginate} is out of range, `PaginateError` is returned. -### get_unused_addresses() -> [Address] +### ergo.get_unused_addresses(): Promise\ + +Errors: APIError -Returns unused addresses available that have not been used yet. The wallet would need to pre-generate addresses here within its restrictions (e.g. discover gap on indices for HD wallets). These can be used for receive addresses. +Returns unused addresses available that have not been used yet. The wallet would need to pre-generate addresses here within its restrictions (e.g. discover gap on indices for HD wallets). -### sign_tx(tx: Tx) -> Promise\ | TxSignError +### ergo.get_change_address(): Promise\
+ +Errors: APIError + +Returns an address owned by the wallet that should be used as a change address to return leftover assets during transaction creation back to the connnected wallet. This can be used as a generic receive address as well. + +### ergo.sign_tx(tx: Tx) -> Promise\ + +Errors: APIError, TxSignError A signed tx is returned if the wallet can provide proofs for all inputs (P2PK ones). If not it produces an error. This should also have some kind of user pop-up which if rejected would produce an error as well. This way signing/keys are kept entirely in the wallet. -### sign_tx_input(tx: Tx, index: number) -> Promise\ | TxSignError +### ergo.sign_tx_input(tx: Tx, index: number): Promise\ + +Errors: APIError, TxSignError Lower level tx signing functionality that signs a single input if the wallet is able to, and returns an error if not. This can be useful for constructing transactions consisting of inputs from multiple parties, and should be general enough for future transaction signing use-cases. Likewise with `sign_tx` this should ask for user consent and can be rejected producing an error. -### sign_data(addr: Address, message: String) -> Promise\ | DataSignError +### ergo.sign_data(addr: Address, message: String): Promise\ + +Errors: APIError, DataSignError Signs the generic data {data} encoded as a hex string using the private key associated with {addr} if possible. If the wallet does not own this address, an error is returned. This should ask for user consent and produce an error if rejected. The wallet should also implement a message signing protocol such as the proposed [EmIP-005](https://github.com/Emurgo/EmIPs/blob/5b00fce84f31eb763892186eb9c88739ec809333/specs/emip-005.md) in order to make this endpoint safer for use, and make it harder for the user to accidentally sign data such as transactions, blocks, etc. Returns the signed data as a hex-encoded string. TBD: Hash algorithm/format to use. -### submit_tx(tx: SignedTx) -> TxId | TxSendError +### ergo.submit_tx(tx: SignedTx): Promise\ -Uses the wallet to send the given tx and returns the TxId for the dApp to track when/if it is included as its own responsibility. This is technically not mandatory but any wallet that is using such a bridge should have this functionality. Wallets can additionally choose to rate-limit this call, and can return an error if they did not or could not send the transaction. +Errors: APIError, TxSendError -### add_external_box(box_id: BoxId) -> bool +Uses the wallet to send the given tx and returns the TxId for the dApp to track when/if it is included as its own responsibility. This is technically not mandatory but any wallet that is using such a bridge should have this functionality. Wallets can additionally choose to rate-limit this call, and can return an error if they did not or could not send the transaction. -Informs the wallet of a box it should track, specifically for P2S/P2SH boxes that the wallet might not be aware of. The wallet does not need to follow this suggestion and can handle how it does this on its own. -Returns true if the box is now tracked (regardless of whether it was tracked before), and false if it is not tracked. ## Data Formats -Most of these types are meant to be the same as the JSON types in the Ergo full node, which matches up as of now with the `sigma-rust` representations. +The initial design for most of these types are meant to be the same as the JSON types in the Ergo full node, which matches up as of now with the `sigma-rust` representations, but these definitions are the definitive types. ### Address @@ -85,23 +109,19 @@ String as the standard base58 encoding of an address. Transaction output box. Format is the same as `sigma-rust`: ``` -{ - boxId: BoxId, - value: Value, - ergoTree: ErgoTree, - assets: [TokenAmount], - additionalRegisters: { String => Constant }, - creationHeight: int, - transactionId: TxId, - index: int, -} +type Box = {| + boxId: BoxId, + value: Value, + ergoTree: ErgoTree, + assets: TokenAmount[], + additionalRegisters: {| [string]: Constant |}, + creationHeight: number, + transactionId: TxId, + index: number, +|}; ``` Additional registers have string ids "R4", "R5" to "R9". -### BoxCandidate - -Same as `Box` without `transactionId` and `index` as those will be computed when the tx is completed. - ### BoxId Hex string of the box id. @@ -112,11 +132,11 @@ Uses `sigma-rust` rep - Hex-encoded bytes for `SigmaSerialize` of a constant. ### ContextExtension -Uses the full node JSON rep (id to hex-encoded Sigma-state value) +Uses the full node JSON rep (id to hex-encoded Sigma-state value). Empty object is for P2PK. ``` -{ - values: { String => String } -} +type ContextExtension = {||} | {| + values: {| [string]: string |} +|}; ``` or the empty `{}` object for P2PK inputs. @@ -128,58 +148,48 @@ Uses `sigma-rust` rep - Hex string representing serialized bytes of the tree usi Read-only input (e.g. oracle usage) that is not spendable. Uses `sigma-rust` JSON rep: ``` -{ - boxId: BoxId, -} +type DataInput = {| + boxId: BoxId, +|}; ``` ### SignedInput A signed `UnsignedInput`. Uses full-node JSON rep: ``` -{ - boxId: BoxId, - spendingProof: ProverResult, - extension: ContextExtension, -} +type SignedInput = {| + boxId: BoxId, + spendingProof: ProverResult, +|}; ``` ### UnsignedInput Uses full-node JSON rep: ``` -{ - boxId: BoxId, - extension: ContextExtension, -} +type UnsignedInput = {| + boxId: BoxId, + extension: ContextExtension, +|}; ``` ## Paginate ``` -{ - page: int, - limit: int, -} +type Paginate = {| + page: number, + limit: number, +|}; ``` Used to specify optional pagination for some API calls. Limits results to {limit} each page, and uses a 0-indexing {page} to refer to which of those pages of {limit} items each. -## PaginateError - -``` -{ - maxSize: int, -} -``` -{maxSize} is the maximum size for pagination and if we try to request pages outside of this boundary this error is thrown. - ### ProverResult Uses `sigma-rust` rep - proof is a byte array serialized using `SigmaSerialize` in hex format: ``` -{ - proofBytes: String, - extension: ContextExtension, -} +type ProverResult = {| + proofBytes: string, + extension: ContextExtension, +|}; ``` ### SignedTx @@ -187,13 +197,13 @@ Uses `sigma-rust` rep - proof is a byte array serialized using `SigmaSerialize` Uses `sigma-rust` rep for a transaction: ``` -{ - id: TxId, - inputs: [SignedInput], - dataInputs: [DataInput], - outputs: [Box], - size: int, -} +type SignedTx = {| + id: TxId, + inputs: SignedInput[], + dataInputs: DataInput[], + outputs: Box[], + size: number, +|}; ``` with the difference with `Tx` is that the inputs are signed (`SignedInput` instead of `UnsignedInput`) @@ -202,10 +212,10 @@ with the difference with `Tx` is that the inputs are signed (`SignedInput` inste Uses `sigma-rust` rep: ``` -{ - tokenId: TokenId, - amount: Value, -} +type TokenAmount = {| + tokenId: TokenId, + amount: Value, +|}; ``` ### TokenId @@ -216,12 +226,12 @@ Uses `sigma-rust` rep - Hex string of the id. An unsigned transaction. Uses a modified version of the transaction representation in `sigma-rust`. ``` -{ - id: TxId, - inputs: [UnsignedInput], - dataInputs: [DataInput], - outputCandidates: [BoxCandidate], -} +type Tx = {| + id: TxId, + inputs: UnsignedInput[], + dataInputs: DataInput[], + outputs: Box[], +|}; ``` This differs from `Tx` in that that `inputs` would be `[UnsignedInput]`, however we haven't signed this tx yet and also the outputs are merely candidates. @@ -229,6 +239,10 @@ This differs from `Tx` in that that `inputs` would be `[UnsignedInput]`, however Same as `sigma-rust` - string hex encoding +### Value + +BigNum-like object. Represents a value which may or may not be ERG. Value is in the smallest possible unit (e.g. nanoErg for ERG). It can be either a `number` or a `string` using standard unsigned integer text representation. + ### TxSendError ``` @@ -282,6 +296,29 @@ DataSignError { * UserDeclined - User declined to sign the data * InvalidFormat - If a wallet enforces data format requirements, this error signifies that the data did not conform to valid formats. -### Value +### APIError -BigNum-like object. Represents a value which may or may not be ERG. Value is in the smallest possible unit (e.g. nanoErg for ERG). It can be either a `number` or a `string` using standard unsigned integer text representation. +``` +APIErrorCode { + InvalidRequest: -1, + InternalError: -2, + Refused: -3, +} +APIError { + code: APIErrorCode, + info: string +} +``` + +* InvalidRequest - Inputs do not conform to this spec or are otherwise invalid. +* InternalError - An error occurred during execution of this API call. +* Refused - The request was refused due to lack of access - e.g. wallet disconnects. + +## PaginateError + +``` +type PaginateError = {| + maxSize: number, +|}; +``` +{maxSize} is the maximum size for pagination and if we try to request pages outside of this boundary this error is thrown. From d0d5d9aa9f7ac3f21897887d3c9f4e8b5f2bb392 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Mon, 31 May 2021 12:44:32 -0500 Subject: [PATCH 4/6] Update Tx (unsigned) The id field has been removed, as well as the tx id/box id from outputs which are now BoxCandidates. This was only used before to help with interop with exisitng tooling but now that sigma-rust no longer requires these there's no reason to keep them around. They only made it more difficult to construct an unsigned transaction. Now one can be a lot more easily constructed even without using other tooling to calculate the IDs/etc. --- eip-0012.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/eip-0012.md b/eip-0012.md index ed3060c4..56069a9a 100644 --- a/eip-0012.md +++ b/eip-0012.md @@ -122,6 +122,20 @@ type Box = {| ``` Additional registers have string ids "R4", "R5" to "R9". +### BoxCandidate + +A candidate (no TX/box IDs) for an output in an `UnsignedTransaction` +``` +type Box = {| + value: Value, + ergoTree: ErgoTree, + assets: TokenAmount[], + additionalRegisters: {| [string]: Constant |}, + creationHeight: number, +|}; +``` +Additional registers have string ids "R4", "R5" to "R9". + ### BoxId Hex string of the box id. @@ -227,13 +241,12 @@ Uses `sigma-rust` rep - Hex string of the id. An unsigned transaction. Uses a modified version of the transaction representation in `sigma-rust`. ``` type Tx = {| - id: TxId, inputs: UnsignedInput[], dataInputs: DataInput[], - outputs: Box[], + outputs: BoxCandidate[], |}; ``` -This differs from `Tx` in that that `inputs` would be `[UnsignedInput]`, however we haven't signed this tx yet and also the outputs are merely candidates. +This differs from `Tx` in that that `inputs` would be `[UnsignedInput]`, there is no ID, and the boxes are just candidates. ### TxId From 3feb01426e72ac9a711b8a26513ddfbcd0c4d312 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Mon, 31 May 2021 12:59:14 -0500 Subject: [PATCH 5/6] Include full box information in UnsignedInput --- eip-0012.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/eip-0012.md b/eip-0012.md index 56069a9a..f92cf391 100644 --- a/eip-0012.md +++ b/eip-0012.md @@ -179,14 +179,25 @@ type SignedInput = {| ### UnsignedInput -Uses full-node JSON rep: +This is the same format as `Box` but with an `extension` field as well. ``` type UnsignedInput = {| - boxId: BoxId, extension: ContextExtension, + boxId: BoxId, + value: Value, + ergoTree: ErgoTree, + assets: TokenAmount[], + additionalRegisters: {| [string]: Constant |}, + creationHeight: number, + transactionId: TxId, + index: number, |}; ``` +Including the entire details of the Box instead of just its id was chosen to facilitate the signing of transactions that the user's wallet does not or can not track. There are two primary cases where this happens: +* The input is P2S and the wallet might not be tracking those as they can be user-agnostic +* The input was a recently created output that might not be accepted by the wallet's backend yet, as would be the case if the dApp just created it. + ## Paginate ``` From b70f406ad9f3cb7088407857820660558250db73 Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Wed, 16 Jun 2021 20:02:28 -0500 Subject: [PATCH 6/6] Update eip-0012.md typo in type name Co-authored-by: Denys Zadorozhnyi --- eip-0012.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eip-0012.md b/eip-0012.md index f92cf391..74d16988 100644 --- a/eip-0012.md +++ b/eip-0012.md @@ -126,7 +126,7 @@ Additional registers have string ids "R4", "R5" to "R9". A candidate (no TX/box IDs) for an output in an `UnsignedTransaction` ``` -type Box = {| +type BoxCandidate = {| value: Value, ergoTree: ErgoTree, assets: TokenAmount[],