Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CHIP-0002: Add dApp protocol #9

Merged
merged 33 commits into from
Oct 25, 2022
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
55a14ce
Add dApp protocol proposal
Apr 20, 2022
21db5ff
update some methods, add rationale & reference implementation setctions
Apr 25, 2022
01b4ffc
update methods and remove createAssetTx
Apr 27, 2022
d0ea2b9
add getPublicKeyByAddress method & additional description
Apr 28, 2022
b4dad7c
change to CHIP0002 & Draft status
Apr 28, 2022
c8b9e60
remove IETF RFC Spec & update Comments-URI
Apr 29, 2022
8543505
Merge branch 'Chia-Network:main' into chip0002
Apr 29, 2022
d3026ea
update methods
Apr 29, 2022
fc9140d
format & add types
May 10, 2022
2868c7d
change to browser.tabs.sendMessage & fix typo
May 12, 2022
53f5763
format
May 13, 2022
5f7a07d
remove Transfer, CreateOffer & TakeOffer
May 15, 2022
ea6d43f
add a `puzzle` return parameter & remove duplicated CoinSpend
May 25, 2022
36b488f
format
May 25, 2022
39fec69
unify function naming
Jun 5, 2022
c18a8dd
update rationale
Jun 5, 2022
67731b5
add more desc & update security section
Jun 7, 2022
47b542b
add example.js
Jun 8, 2022
aaa1c61
support NFT & DID
Jun 16, 2022
7d8ff8d
format
Jun 16, 2022
20c9980
simplify the APIs & add pagination
Jun 25, 2022
1953e38
update `getPublicKeys` desc
Jun 28, 2022
71820aa
fix typos
Jul 10, 2022
fc8be6a
detect wallet name
Jul 12, 2022
eef816d
add apiVersion & wallet version
Jul 17, 2022
c9f162f
add newly-defined functions in example.js
Aug 10, 2022
c75136c
change to Review status
Aug 17, 2022
1a67cbf
change pagination of getAssetCoins
Aug 26, 2022
061de4c
suppor partialSign
Sep 5, 2022
5fae81d
update to Last Call
Sep 13, 2022
73061c3
change signMessage method & revert to Review status
Oct 1, 2022
35e9f82
update to Last Call
Oct 11, 2022
7434568
Change status of 0002
Oct 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
350 changes: 350 additions & 0 deletions CHIPs/chip-0002.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,350 @@
CHIP Number | 0002
:-------------|:----
Title | dApp protocol
Description | This proposal describes a Web3 bridge between the browser wallets and dApps on the Chia Network.
Author | [Dimitry Suen](https://github.com/dimitrysuen)
Comments-URI | https://github.com/Chia-Network/chips/pull/9
Status | Draft
Category | Process
Sub-Category | Procedural
Created | 2022-04-19
Requires | None
Replaces | None
Superseded-By | None

## Abstract

This proposal describes a Web3 bridge between the browser wallets and dApps on the Chia Network.

## Motivation

More and more decentralized apps are emerging on the Chia Network. As an important entrance to Web3, a user-friendly plug-in browser wallet makes it easier for users to interact on the Chia Network. Meanwhile, dApps require access to call the user's wallet, typically from a web context.

We propose this dApp protocol for discussion with developers to improve. We hope that Chia Network's browser plug-in wallets will follow this standard to simplify the integration development of dApps.

## Backwards Compatibility

The parameters are currently passed via `JSON`, which can be modified for subsequent expansion.

## Rationale

To keep the protocol simple and flexible, we do not encapsulate the offer-related and transfer methods, but provide an underlying method called `signTransaction`. With a mature javascript/typescript library, the integration experience of wallets for dApp devs would be easy, e.g., the library can provide more features like type hint and change wallet provider.

## Goal

We manage our users' coins and private keys while maintaining the security and privacy of their funds. Our primary principle is to protect the assets' security and privacy and maintain compatibility with Chia Wallet as much as possible.

## Requirements

None.

## Specification

All methods can be called via `window.chia`, such as

```tsx
interface RequestArguments {
method: string;
params?: object;
}
window.chia.request(args: RequestArguments): Promise<any>;
mariano54 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make it clear that any request might take a while (Several minutes) because the user has to approve manually. Is this true?

```

The dApps and wallet use `browser.tabs.sendMessage` to communicate, the `request` function is a wrapper for the `sendMessage` function.
This proposal aims to specify an underlying API that third-party libraries can wrap like "wallet.reqestAddresses()" so that they can provide additional features, such as type hint.

## Methods

### addresses

Return a list of `standard puzzleHash`. The number of addresses returned depends on the wallet implementation. If dApp is not approved, API will return an empty list.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we define standard puzzle hash? If the wallet wants to use a different type of puzzle than another wallet, that's fine right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should just say: XCH address. XCH will be sent directly to this address, so this should not be a wrapper CAT address, for example

The dApp can build special CAT puzzleHash from the `standard puzzleHash`. The wallet can support one root key or multi-root keys either. Even in watch-only mode, the wallet can work without any root key. And the wallet can combine the addresses derived from different private keys. However, we recommend that different private keys be used separately from privacy concerns.

There is no restriction on the number of addresses the method returns. In some cases, the wallet can use many addresses internally and return 2-3 addresses.

```tsx
addresses(): string[]
```

### chainId

Return the current chainID.

| CHAIN NAME | CHAINID |
|------------|-----------|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might want to specify "Chia Mainnet" on the left

| Mainnet | mainnet |
| Testnet10 | testnet10 |

```tsx
chainId(): string
```

### connect

mariano54 marked this conversation as resolved.
Show resolved Hide resolved
The dApp requests users' permission to connect the wallet. If the user rejects the request, the API will throw `userRejectdRequestError`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does connect imply? Does this give read access to all the users data? It definitely does not give write access, correct?


```tsx
connect(): void
```

### walletSwitchChain

dApps request to switch to another chain

```tsx
walletSwitchChain(params: {chainId: string}): void
```

### selectAssetCoins

API returns the spendable coins for the selected assets. API will return all the available coins if the amount exceeds the spendable amount.

When `type` is `did` or `nft`, the assetId can be null, which means no restrictions, and the API returns the corresponding type of coins. Also, when `type` is `cat`, `did` or `nft`, API will return an extra field called `lineageProof` to facilitate dApps to build `coinSpend` easily. In some cases, users may have many small amounts of coins. The wallet can allow users to choose whether to filter the coins.

DApp can parse NFT and DID info from `puzzle` as needed.

Parameters as described below.
| Parameter | Description |
|-----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| type | Asset type: `null`/cat/did/nft |
| assetId | It depends on `type` and will be ignored when type is `null`. If type is `cat`, `assetId` means `tail program hash`. If `type` is `did`, `assetId` means `did id`. If `type` is `nft`, `assetId` means `nft id` |
| amount | This is optional. All the coins(including locked coins) will be returned if it is missing. Otherwise, API will return the unlocked coins only. |
| excludes | This is optional. This value is the `name` of coins. |

```tsx
interface SelectAssetCoinsParams {
type: string|null;
assetId: string|null;
amount?: number|string;
excludes?: string[];
}

interface CoinRecord {
coin: Coin;
coinName: string;
puzzle: string;
confirmedBlockIndex: number;
timestamp: number;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need the timestamp?

locked: boolean;
lineageProof?: {
parentName?: string;
innerPuzzleHash?: string;
amount?: number;
}
}

selectAssetCoins(params: SelectAssetCoinsParams): CoinRecord[]
```

### getAssetBalance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a convenience method, because we can call selectAssetCoins and add up the amount right?


Returns the spendable balance of the wallet. It's convenient for the dApp to query the user's balance.

When `type` is `did` or `nft`, `assetId` can be `null`, which means no restriction. And API will return corresponding type.

Parameters as described below.
| Parameter | Description |
|-----------|-----------------------------------------|
| type | same as `type` in `selectAssetCoins` |
| assetId | same as `assetId` in `selectAssetCoins` |

```tsx
interface AssetBalanceResp {
confirmed: string;
spendable: string;
spendableCoinCount: number;
}

getAssetBalance(params: {type: string|null, assetId: string|null}): AssetBalanceResp
```

### getPublicKeyByAddress
return the public key asscociated with the address. If the address or the public key doesn't exist, API will throw an exception.

Parameters as described below.
| Parameter | Description |
|-----------|-----------------------------------|
| address | the data from method `addresses` |

```tsx
getPublicKeyByAddress(params: {address: string}): string
```

### signTransaction

This is a lower-level API that signs custom coin spends. API return a signed `SpendBundle`. Besides users' coins, it also supports the coins not owned by the user. For security purposes, the wallet should check if `coin.puzzle_hash` and `hash(puzzle_reveal)` are equal and the `conditions` generated by the coin satisfy the specification. The wallet will track the signed transactions. The coins in the transaction will be locked and not be returned in `selectAssetCoins`.

Please note that, for security purposes, the `AGG_SIGN_UNSAFE` in the `conditions` will be ignored.

Parameters as described below.
| Parameter | Description |
|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
| coinSpends | a list of `coinSpend` |
| coinSpend.coin | the value is `Coin` |
| coinSpend.puzzle_reveal | the puzzle of the Coin |
| coinSpend.solution | the solution of puzzle |
| broadcast | this is optional. It means whether the wallet broadcasts the tx to the full node. If the value is `True`, an Error will be thrown if the tx is invalid. |

```tsx
interface SignTransactionParams {
coinSpends: CoinSpend[];
broadcast?: boolean;
}

interface SignTransactionResp {
id: string;
transaction: SpendBundle;
}

signTransaction(params: SignTransactionParams): SignTransactionResp
```

### signMessage

Sign the message encoded as a hex string using the private key associated with the address's private key.
The internal implementation in the wallet is `bls_sign(private_key, sha256("\x18Chia Signed Message:\n" + len(message) + message))`. To prevent replay attacks, dApps should add the current `networkId` and `timestamp` into the message. If the dApp doesn't care which chain they're on, they can include the `timestamp` only.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For len(message) values that don't fit in a single byte, is there an expected byte ordering here? Network byte order would be what I would expect.


Parameters as described below.
| Parameter | Description |
|-----------|-----------------------------------|
| message | the hex string needs to be signed |
| address | the data from method `addresses` |

```tsx
interface SignMessageParams {
message: string;
address: string;
}

interface SignMessageResp {
publicKey: string;
signature: string;
}

signMessage(params: SignMessageParams): SignMessageResp
```

### pushTransaction

Even if the wallet supports `pushTransaction`, we still highly recommend that the dApp uses its full node to broadcast transactions.

```tsx
interface PushTransactionParams {
spendBundle: SpendBundle;
}

interface PushTransactionResp {
// mempool status, success/pending/failed
status: string;
dimitrysuen marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a list of statuses, because it's possible the wallet might be connected to multiple nodes in the future.

}

pushTransaction(params: PushTransactionParams): PushTransactionResp
```

## Events

### chainChanged

the bridge emits `chainChanged` when connecting to a new chain

```tsx
chia.on('chainChanged', listener: (chainId: string) => void)
```

### addressesChanged

the bridge emits `addressesChanged` if the addresses change account in the wallet
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this mean? Does it mean a new address was created? or the active key was changed?


```tsx
chia.on('addressesChanged', listener: (addresses: string[]) => void)
```

mariano54 marked this conversation as resolved.
Show resolved Hide resolved
## Types

```tsx
interface Coin {
parent_coin_info: string;
puzzle_hash: string;
amount: number;
}

interface CoinSpend {
coin: Coin;
puzzle_reveal: string;
solution: string;
}

interface SpendBundle {
coin_spends: CoinSpend[];
aggregated_signature: string;
}
```

## Errors

```tsx
interface Error {
code: number;
message: string;
data?: any;
}
```

```tsx
invalidParamsError = {
code: 4000,
message: 'invalidParams'
}

unauthorizedError = {
code: 4001,
message: "unauthorized"
}

userRejectdRequestError = {
code: 4002,
message: "userRejectdRequest"
}

spendableBalanceExceeded = {
code: 4003,
message: 'spendableBalanceExceeded',
}

limitExceed = {
code: 4029,
message: 'too many requests'
}
```

## Test Cases

See [example.js](/notes/0002/example.js).

## Security

1. access control

Except for `chainId`, `addresses` and `connect`, the dApp needs the read permission of the method before calling it.

2. approval

`signTransaction`, `signMessage`, `walletSwitchChain` and `getPublicKeyByAddress` methods need to be approved by the user before calling.

## Additional Assets

None.

## Reference Implementation

The [Goby](https://goby.app) team has implemented the methods described above.

1. https://github.com/Chia-Network/chia-blockchain/blob/main/chia/rpc/wallet_rpc_api.py
2. https://github.com/ChainSafe/web3.js
3. https://github.com/cardano-foundation/CIPs/tree/master/CIP-0030
4. https://vacuumlabs.github.io/ledgerjs-cardano-shelley/5.0.0/index.html
5. https://github.com/solana-labs/wallet-adapter

## Copyright

Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
Loading