You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Upgrade CIP-0030 to latest format & propose version / extension scheme
This has been a recurring topic regarding extensions of CIP-0030.
Given that this CIP is effectively active (hence the status change in
this commit), we can't simply keep modifying it because it then
becomes hard for DApps to keep track of what is supported by wallet
providers and what isn't.
In this commit, I henceforth propose an extension scheme based off
CIPs. Wallet providers are expected to support the base API given by
CIP-0030 and new extensions or modifications of that API can be
brought in through extension CIPs. Upon initialization, Dapps can
interrogate the wallet about what CIPs extensions it supports and
process or fail accordingly based on whether the wallet provides the
desired functionality.
This documents describes a webpage-based communication bridge allowing webpages (i.e. dApps) to interface with Cardano wallets. This is done via injected javascript code into webpages. This specification defines the manner that such code is to be accessed by the webpage/dApp, as well as defining the API for dApps to communicate with the user's wallet. This document currently concerns the Shelley-Mary era but will have a second version once Plutus is supported. This specification is intended to cover similar use cases as web3 for Ethereum or [EIP-0012](https://github.com/ergoplatform/eips/pull/23) for Ergo. The design of this spec was based on the latter.
15
39
16
-
17
-
# Motivation
40
+
## Motivation: why is this CIP necessary?
18
41
19
42
In order to facilitate future dApp development, we will need a way for dApps to communicate with the user's wallet. While Cardano does not yet support smart contracts, there are still various use cases for this, such as NFT management. This will also lay the groundwork for an updated version of the spec once the Alonzo hardfork is released which can extend it to allow for Plutus support.
20
43
44
+
## Specification
21
45
46
+
### Data Types
22
47
23
-
# Specification
24
-
25
-
## Version
26
-
27
-
The API specified in this document will count as version 0.1.0 for version-checking purposes below.
28
-
29
-
## Data Types
30
-
31
-
### Address
48
+
#### Address
32
49
33
50
A string representing an address in either bech32 format, or hex-encoded bytes. All return types containing `Address` must return the bech32 format, but must accept either format for inputs.
34
51
35
-
### Bytes
52
+
####Bytes
36
53
37
54
A hex-encoded string of the corresponding bytes.
38
55
39
-
### cbor\<T>
56
+
####cbor\<T>
40
57
41
58
A hex-encoded string representing [CBOR](https://tools.ietf.org/html/rfc7049) corresponding to `T` defined via [CDDL](https://tools.ietf.org/html/rfc8610) either inside of the [Shelley Multi-asset binary spec](https://github.com/input-output-hk/cardano-ledger-specs/blob/0738804155245062f05e2f355fadd1d16f04cd56/shelley-ma/shelley-ma-test/cddl-files/shelley-ma.cddl) or, if not present there, from the [CIP-0008 signing spec](https://github.com/cardano-foundation/CIPs/blob/master/CIP-0008/CIP-0008.md).
42
59
This representation was chosen when possible as it is consistent across the Cardano ecosystem and widely used by other tools, such as [cardano-serialization-lib](https://github.com/Emurgo/cardano-serialization-lib), which has support to encode every type in the binary spec as CBOR bytes.
43
60
44
-
### DataSignature
61
+
####DataSignature
45
62
46
63
```
47
64
type DataSignature = {|
48
-
signature:cbor\<COSE_Sign1>,
65
+
signature:cbor\<COSE_Sign1>,
49
66
key: cbor\<COSE_Key>,
50
67
|};
51
68
```
52
69
53
-
### TransactionUnspentOutput
70
+
####TransactionUnspentOutput
54
71
55
72
If we have CBOR specified by the following CDDL referencing the Shelley-MA CDDL:
73
+
56
74
```cddl
57
75
transaction_unspent_output = [
58
76
input: transaction_input,
59
77
output: transaction_output,
60
78
]
61
79
```
80
+
62
81
then we define
82
+
63
83
```
64
84
type TransactionUnspentOutput = cbor<transaction_unspent_output>
65
85
```
66
86
67
87
This allows us to use the output for constructing new transactions using it as an output as the `transaction_output` in the Shelley Multi-asset CDDL does not contain enough information on its own to spend it.
68
88
69
-
### Paginate
89
+
####Paginate
70
90
71
91
```
72
92
type Paginate = {|
@@ -76,10 +96,19 @@ type Paginate = {|
76
96
```
77
97
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. dApps should be aware that if a wallet is modified between paginated calls that this will change the pagination, e.g. some results skipped or showing up multiple times but otherwise the wallet must respect the pagination order.
78
98
99
+
#### Extension
100
+
101
+
An extension is a `String` that describe a CIP number extending the API. The format expected for each of these extension string is:
102
+
103
+
```
104
+
CIP-XXXX
105
+
```
106
+
107
+
where `XXXX` is a CIP number left-padded with zero (e.g. `CIP-0030`).
79
108
80
-
## Error Types
109
+
###Error Types
81
110
82
-
### APIError
111
+
####APIError
83
112
84
113
```
85
114
APIErrorCode {
@@ -98,8 +127,10 @@ APIError {
98
127
* InternalError - An error occurred during execution of this API call.
99
128
* Refused - The request was refused due to lack of access - e.g. wallet disconnects.
100
129
* AccountChange - The account has changed. The dApp should call `wallet.enable()` to reestablish connection to the new account. The wallet should not ask for confirmation as the user was the one who initiated the account change in the first place.
130
+
* UnsupportedExtension - The dApp requested an extension that is not supported by the wallet.
131
+
* IncompatibleExtensions - The dApp requested two (or more) extensions that are possibly incompatible and the wallet refuses to proceed.
101
132
102
-
### DataSignError
133
+
####DataSignError
103
134
104
135
```
105
136
DataSignErrorCode {
@@ -117,7 +148,7 @@ type DataSignError = {
117
148
* AddressNotPK - Address was not a P2PK address and thus had no SK associated with it.
118
149
* UserDeclined - User declined to sign the data
119
150
120
-
### PaginateError
151
+
####PaginateError
121
152
122
153
```
123
154
type PaginateError = {|
@@ -126,7 +157,7 @@ type PaginateError = {|
126
157
```
127
158
{maxSize} is the maximum size for pagination and if the dApp tries to request pages outside of this boundary this error is thrown.
128
159
129
-
### TxSendError
160
+
####TxSendError
130
161
131
162
```
132
163
TxSendErrorCode = {
@@ -142,7 +173,7 @@ type TxSendError = {
142
173
* Refused - Wallet refuses to send the tx (could be rate limiting)
143
174
* Failure - Wallet could not send the tx
144
175
145
-
### TxSignError
176
+
####TxSignError
146
177
147
178
```
148
179
TxSignErrorCode = {
@@ -158,57 +189,57 @@ type TxSignError = {
158
189
* 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)
159
190
* UserDeclined - User declined to sign the transaction
160
191
161
-
162
-
163
-
## Initial API
192
+
### Initial API
164
193
165
194
In order to initiate communication from webpages to a user's Cardano wallet, the wallet must provide the following javascript API to the webpage. A shared, namespaced `cardano` object must be injected into the page if it did not exist already. Each wallet implementing this standard must then create a field in this object with a name unique to each wallet containing a `wallet` object with the following methods. The API is split into two stages to maintain the user's privacy, as the user will have to consent to `cardano.{walletName}.enable()` in order for the dApp to read any information pertaining to the user's wallet with `{walletName}` corresponding to the wallet's namespaced name of its choice.
This is the entrypoint to start communication with the user's wallet. The wallet should request the user's permission to connect the web page to the user's wallet, and if permission has been granted, the full API will be returned to the dApp to use. The wallet can choose to maintain a whitelist to not necessarily ask the user's permission every time access is requested, but this behavior is up to the wallet and should be transparent to web pages using this API. If a wallet is already connected this function should not request access a second time, and instead just return the `API` object.
Upon start, dApp can explicitly request a list of additional functionalities they expect as a list of CIP numbers capturing those extensions. This is used as an extensibility mechanism to document what functionalities can be provided by the wallet interface. CIP-0030 provides a set of base interfaces that every wallet must support. Then, new functionalities are introduced via additional CIPs and may be all or partially supported by wallets.
203
+
204
+
DApps are expected to use this endpoint to perform an initial handshake and ensure that the wallet supports all their required functionalities; or inform the user accordingly. Note that it's possible for two extensions to be mutually incompatible (because they provide two conflicting features). While we may try to avoid this as much as possible while designing CIPs, it is also the responsability of wallet providers to assess whether they can support a given combination of extensions, or not. Wallet should therefore use the respective `UnsupportedExtension` or `IncompatibleExtensions` error codes to inform the dApp about the outcome.
Returns true if the dApp is already connected to the user's wallet, or if requesting access would return true without user confirmation (e.g. the dApp is whitelisted), and false otherwise. If this function returns true, then any subsequent calls to `wallet.enable()` during the current session should succeed and return the `API` object.
178
211
179
-
### cardano.{walletName}.apiVersion: String
212
+
####cardano.{walletName}.apiVersion: String
180
213
181
-
The version number of the API that the wallet supports.
214
+
The version number of the API that the wallet supports. Set to `1`.
182
215
183
-
184
-
### cardano.{walletName}.name: String
216
+
#### cardano.{walletName}.name: String
185
217
186
218
A name for the wallet which can be used inside of the dApp for the purpose of asking the user which wallet they would like to connect with.
187
219
188
-
### cardano.{walletName}.icon: String
220
+
####cardano.{walletName}.icon: String
189
221
190
222
A URI image (e.g. data URI base64 or other) for img src for the wallet which can be used inside of the dApp for the purpose of asking the user which wallet they would like to connect with.
191
223
192
-
193
-
## Full API
224
+
### Full API
194
225
195
226
Upon successful connection via `cardano.{walletName}.enable()`, a javascript object we will refer to as `API` (type) / `api` (instance) is returned to the dApp with the following methods. All read-only methods (all but the signing functionality) should not require any user interaction as the user has already consented to the dApp reading information about the wallet's state when they agreed to `cardano.{walletName}.enable()`. The remaining methods `api.signTx()` and `api.signData()` must request the user's consent in an informative way for each and every API call in order to maintain security.
196
227
197
228
The API chosen here is for the minimum API necessary for dApp <-> Wallet interactions without convenience functions that don't strictly need the wallet's state to work. The API here is for now also only designed for Shelley's Mary hardfork and thus has NFT support. When Alonzo is released with Plutus support this API will have to be extended.
198
229
199
-
### api.getNetworkId(): Promise\<number>
230
+
####api.getNetworkId(): Promise\<number>
200
231
201
232
Errors: `APIError`
202
233
203
234
Returns the network id of the currently connected account. 0 is testnet and 1 is mainnet but other networks can possibly be returned by wallets. Those other network ID values are not governed by this document. This result will stay the same unless the connected account has changed.
If `amount` is `undefined`, this shall return a list of all UTXOs (unspent transaction outputs) controlled by the wallet. If `amount` is not `undefined`, this request shall be limited to just the UTXOs that are required to reach the combined ADA/multiasset value target specified in `amount`, and if this cannot be attained, `null` shall be returned. The results can be further paginated by `paginate` if it is not `undefined`.
@@ -225,43 +256,43 @@ The main point is to allow the wallet to encapsulate all the logic required to h
225
256
226
257
The `amount` parameter is required, specified as a `string` (BigNumber) or a `number`, and the maximum allowed value must be agreed to be something like 5 ADA. Not limiting the maximum possible value might force the wallet to attempt to purify an unreasonable amount of ADA just because the dapp is doing something weird. Since by protocol the required collateral amount is always a percentage of the transaction fee, it seems that the 5 ADA limit should be enough for the foreseeable future.
227
258
228
-
### api.getBalance(): Promise\<cbor\<value>>
259
+
####api.getBalance(): Promise\<cbor\<value>>
229
260
230
261
Errors: `APIError`
231
262
232
263
Returns the total balance available of the wallet. This is the same as summing the results of `api.getUtxos()`, but it is both useful to dApps and likely already maintained by the implementing wallet in a more efficient manner so it has been included in the API as well.
Returns a list of all used (included in some on-chain transaction) addresses controlled by the wallet. The results can be further paginated by `paginate` if it is not `undefined`.
239
270
240
-
### api.getUnusedAddresses(): Promise\<Address[]>
271
+
####api.getUnusedAddresses(): Promise\<Address[]>
241
272
242
273
Errors: `APIError`
243
274
244
275
Returns a list of unused addresses controlled by the wallet.
245
276
246
-
### api.getChangeAddress(): Promise\<Address>
277
+
####api.getChangeAddress(): Promise\<Address>
247
278
248
279
Errors: `APIError`
249
280
250
281
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 connected wallet. This can be used as a generic receive address as well.
251
282
252
-
### api.getRewardAddresses(): Promise\<Address[]>
283
+
####api.getRewardAddresses(): Promise\<Address[]>
253
284
254
285
Errors: `APIError`
255
286
256
287
Returns the reward addresses owned by the wallet. This can return multiple addresses e.g. CIP-0018.
Requests that a user sign the unsigned portions of the supplied transaction. The wallet should ask the user for permission, and if given, try to sign the supplied body and return a signed transaction. If `partialSign` is true, the wallet only tries to sign what it can. If `partialSign` is false and the wallet could not sign the entire transaction, `TxSignError` shall be returned with the `ProofGeneration` code. Likewise if the user declined in either case it shall return the `UserDeclined` code. Only the portions of the witness set that were signed as a result of this call are returned to encourage dApps to verify the contents returned by this endpoint while building the final transaction.
As wallets should already have this ability, we allow dApps to request that a transaction be sent through it. If the wallet accepts the transaction and tries to send it, it shall return the transaction id for the dApp to track. The wallet is free to return the `TxSendError` with code `Refused` if they do not wish to send it, or `Failure` if there was an error in sending it (e.g. preliminary checks failed on signatures).
289
320
290
-
## Experimental API
321
+
###Experimental API
291
322
292
323
Multiple experimental namespaces are used:
293
324
- under `api` (ex: `api.experimental.myFunctionality`).
@@ -299,8 +330,31 @@ The benefits of this are:
299
330
1. New features can be added to CIP30 as experimental features and only moved to non-experimental once multiple wallets implement it
300
331
1. It provides a clear path to updating the CIP version number (when functions move from experimental -> stable)
301
332
302
-
# Implementations
333
+
## Rationale: how does this CIP achieve its goals?
334
+
335
+
See justification and explanations provided with each API endpoint.
336
+
337
+
## Path to Active
338
+
339
+
### Acceptance Criteria
340
+
341
+
-[x] The interface is implemented and supported by various wallet providers. See also: [cardano-caniuse](https://www.cardano-caniuse.io/).
342
+
-[x] The interface is used by DApps to interact with wallet providers. Few examples:
343
+
-https://www.jpg.store/
344
+
-https://app.minswap.org/
345
+
-https://muesliswap.com/
346
+
-https://exchange.sundaeswap.finance/
347
+
-https://app.indigoprotocol.io/
348
+
349
+
### Implementation Plan
350
+
351
+
-[x] Provide some reference implementation of wallet providers
0 commit comments