Skip to content

Commit a325ea8

Browse files
committed
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.
1 parent d2b5b16 commit a325ea8

File tree

1 file changed

+109
-55
lines changed

1 file changed

+109
-55
lines changed

CIP-0030/README.md

+109-55
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,92 @@
11
---
22
CIP: 30
33
Title: Cardano dApp-Wallet Web Bridge
4-
Authors: rooooooooob
5-
Comments-URI: https://github.com/cardano-foundation/CIPs/pull/88
6-
Status: Draft
7-
Type: Standards
4+
Status: Active
5+
Category: Wallets
6+
Authors:
7+
- rooooooooob
8+
Implementors:
9+
- Begin <https://begin.is/>
10+
- Eternl <https://eternl.io/>
11+
- Flint <https://flint-wallet.com/>
12+
- GeroWallet <https://www.gerowallet.io/>
13+
- Lace <https://www.lace.io/>
14+
- Nami <https://namiwallet.io/>
15+
- NuFi <https://nu.fi/>
16+
- RayWallet <https://raywallet.io/>
17+
- Yoroi <https://yoroi-wallet.com/>
18+
Discussions:
19+
- https://github.com/cardano-foundation/CIPs/pull/88
20+
- https://github.com/cardano-foundation/CIPs/pull/148
21+
- https://github.com/cardano-foundation/CIPs/pull/151
22+
- https://github.com/cardano-foundation/CIPs/pull/183
23+
- https://github.com/cardano-foundation/CIPs/pull/208
24+
- https://github.com/cardano-foundation/CIPs/pull/323
25+
- https://github.com/cardano-foundation/CIPs/issues/169
26+
- https://github.com/cardano-foundation/CIPs/issues/178
27+
- https://github.com/cardano-foundation/CIPs/issues/204
28+
- https://github.com/cardano-foundation/CIPs/issues/253
29+
- https://github.com/cardano-foundation/CIPs/issues/386
30+
- https://github.com/cardano-foundation/CIPs/issues/404
31+
- https://github.com/cardano-foundation/CIPs/issues/419
832
Created: 2021-04-29
933
License: CC-BY-4.0
1034
---
1135

12-
# Abstract
36+
## Abstract
1337

1438
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.
1539

16-
17-
# Motivation
40+
## Motivation: why is this CIP necessary?
1841

1942
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.
2043

44+
## Specification
2145

46+
### Data Types
2247

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
3249

3350
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.
3451

35-
### Bytes
52+
#### Bytes
3653

3754
A hex-encoded string of the corresponding bytes.
3855

39-
### cbor\<T>
56+
#### cbor\<T>
4057

4158
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).
4259
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.
4360

44-
### DataSignature
61+
#### DataSignature
4562

4663
```
4764
type DataSignature = {|
48-
signature:cbor\<COSE_Sign1>,
65+
signature: cbor\<COSE_Sign1>,
4966
key: cbor\<COSE_Key>,
5067
|};
5168
```
5269

53-
### TransactionUnspentOutput
70+
#### TransactionUnspentOutput
5471

5572
If we have CBOR specified by the following CDDL referencing the Shelley-MA CDDL:
73+
5674
```cddl
5775
transaction_unspent_output = [
5876
input: transaction_input,
5977
output: transaction_output,
6078
]
6179
```
80+
6281
then we define
82+
6383
```
6484
type TransactionUnspentOutput = cbor<transaction_unspent_output>
6585
```
6686

6787
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.
6888

69-
### Paginate
89+
#### Paginate
7090

7191
```
7292
type Paginate = {|
@@ -76,10 +96,19 @@ type Paginate = {|
7696
```
7797
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.
7898

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`).
79108

80-
## Error Types
109+
### Error Types
81110

82-
### APIError
111+
#### APIError
83112

84113
```
85114
APIErrorCode {
@@ -98,8 +127,10 @@ APIError {
98127
* InternalError - An error occurred during execution of this API call.
99128
* Refused - The request was refused due to lack of access - e.g. wallet disconnects.
100129
* 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.
101132

102-
### DataSignError
133+
#### DataSignError
103134

104135
```
105136
DataSignErrorCode {
@@ -117,7 +148,7 @@ type DataSignError = {
117148
* AddressNotPK - Address was not a P2PK address and thus had no SK associated with it.
118149
* UserDeclined - User declined to sign the data
119150

120-
### PaginateError
151+
#### PaginateError
121152

122153
```
123154
type PaginateError = {|
@@ -126,7 +157,7 @@ type PaginateError = {|
126157
```
127158
{maxSize} is the maximum size for pagination and if the dApp tries to request pages outside of this boundary this error is thrown.
128159

129-
### TxSendError
160+
#### TxSendError
130161

131162
```
132163
TxSendErrorCode = {
@@ -142,7 +173,7 @@ type TxSendError = {
142173
* Refused - Wallet refuses to send the tx (could be rate limiting)
143174
* Failure - Wallet could not send the tx
144175

145-
### TxSignError
176+
#### TxSignError
146177

147178
```
148179
TxSignErrorCode = {
@@ -158,57 +189,57 @@ type TxSignError = {
158189
* 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)
159190
* UserDeclined - User declined to sign the transaction
160191

161-
162-
163-
## Initial API
192+
### Initial API
164193

165194
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.
166195

167-
### cardano.{walletName}.enable(): Promise\<API>
196+
#### cardano.{walletName}.enable(extensions: Extension[] = []): Promise\<API>
168197

169198
Errors: APIError
170199

171200
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.
172201

173-
### cardano.{walletName}.isEnabled(): Promise\<bool>
202+
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.
205+
206+
#### cardano.{walletName}.isEnabled(): Promise\<bool>
174207

175208
Errors: APIError
176209

177210
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.
178211

179-
### cardano.{walletName}.apiVersion: String
212+
#### cardano.{walletName}.apiVersion: String
180213

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`.
182215

183-
184-
### cardano.{walletName}.name: String
216+
#### cardano.{walletName}.name: String
185217

186218
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.
187219

188-
### cardano.{walletName}.icon: String
220+
#### cardano.{walletName}.icon: String
189221

190222
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.
191223

192-
193-
## Full API
224+
### Full API
194225

195226
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.
196227

197228
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.
198229

199-
### api.getNetworkId(): Promise\<number>
230+
#### api.getNetworkId(): Promise\<number>
200231

201232
Errors: `APIError`
202233

203234
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.
204235

205-
### api.getUtxos(amount: cbor\<value> = undefined, paginate: Paginate = undefined): Promise\<TransactionUnspentOutput[] | null>
236+
#### api.getUtxos(amount: cbor\<value> = undefined, paginate: Paginate = undefined): Promise\<TransactionUnspentOutput[] | null>
206237

207238
Errors: `APIError`, `PaginateError`
208239

209240
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`.
210241

211-
### api.getCollateral(params: { amount: cbor\<Coin> }): Promise\<TransactionUnspentOutput[] | null>
242+
#### api.getCollateral(params: { amount: cbor\<Coin> }): Promise\<TransactionUnspentOutput[] | null>
212243

213244
Errors: `APIError`
214245

@@ -225,43 +256,43 @@ The main point is to allow the wallet to encapsulate all the logic required to h
225256

226257
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.
227258

228-
### api.getBalance(): Promise\<cbor\<value>>
259+
#### api.getBalance(): Promise\<cbor\<value>>
229260

230261
Errors: `APIError`
231262

232263
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.
233264

234-
### api.getUsedAddresses(paginate: Paginate = undefined): Promise\<Address[]>
265+
#### api.getUsedAddresses(paginate: Paginate = undefined): Promise\<Address[]>
235266

236267
Errors: `APIError`
237268

238269
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`.
239270

240-
### api.getUnusedAddresses(): Promise\<Address[]>
271+
#### api.getUnusedAddresses(): Promise\<Address[]>
241272

242273
Errors: `APIError`
243274

244275
Returns a list of unused addresses controlled by the wallet.
245276

246-
### api.getChangeAddress(): Promise\<Address>
277+
#### api.getChangeAddress(): Promise\<Address>
247278

248279
Errors: `APIError`
249280

250281
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.
251282

252-
### api.getRewardAddresses(): Promise\<Address[]>
283+
#### api.getRewardAddresses(): Promise\<Address[]>
253284

254285
Errors: `APIError`
255286

256287
Returns the reward addresses owned by the wallet. This can return multiple addresses e.g. CIP-0018.
257288

258-
### api.signTx(tx: cbor\<transaction>, partialSign: bool = false): Promise\<cbor\<transaction_witness_set>>
289+
#### api.signTx(tx: cbor\<transaction>, partialSign: bool = false): Promise\<cbor\<transaction_witness_set>>
259290

260291
Errors: `APIError`, `TxSignError`
261292

262293
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.
263294

264-
### api.signData(addr: Address, payload: Bytes): Promise\<DataSignature>
295+
#### api.signData(addr: Address, payload: Bytes): Promise\<DataSignature>
265296

266297
Errors: `APIError`, `DataSignError`
267298

@@ -281,13 +312,13 @@ If the payment key for `addr` is not a P2Pk address then `DataSignError` will be
281312
* `crv` (-1) - must be set to `Ed25519` (6)
282313
* `x` (-2) - must be set to the public key bytes of the key used to sign the `Sig_structure`
283314

284-
### api.submitTx(tx: cbor\<transaction>): Promise\<hash32>
315+
#### api.submitTx(tx: cbor\<transaction>): Promise\<hash32>
285316

286317
Errors: `APIError`, `TxSendError`
287318

288319
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).
289320

290-
## Experimental API
321+
### Experimental API
291322

292323
Multiple experimental namespaces are used:
293324
- under `api` (ex: `api.experimental.myFunctionality`).
@@ -299,8 +330,31 @@ The benefits of this are:
299330
1. New features can be added to CIP30 as experimental features and only moved to non-experimental once multiple wallets implement it
300331
1. It provides a clear path to updating the CIP version number (when functions move from experimental -> stable)
301332

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
352+
- [Berry-Pool/nami-wallet](https://github.com/Berry-Pool/nami-wallet/blob/master/src/pages/Content/injected.js)
353+
- [Emurgo/yoroi-wallet](https://github.com/Emurgo/yoroi-frontend/blob/develop/packages/yoroi-ergo-connector/src/inject.js)
354+
355+
- [x] Provide some reference implementation of the dapp connector
356+
- [cardano-foundation/connect-with-wallet](https://github.com/cardano-foundation/cardano-connect-with-wallet)
303357

304-
[nami-wallet](https://github.com/Berry-Pool/nami-wallet/blob/master/src/pages/Content/injected.js)
358+
## Copyright
305359

306-
[yoroi-wallet](https://github.com/Emurgo/yoroi-frontend/blob/develop/packages/yoroi-ergo-connector/src/inject.js)
360+
This CIP is licensed under [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/legalcode).

0 commit comments

Comments
 (0)