diff --git a/CHIPs/chip-0002.md b/CHIPs/chip-0002.md new file mode 100644 index 00000000..811d253b --- /dev/null +++ b/CHIPs/chip-0002.md @@ -0,0 +1,415 @@ +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 | +Status | draft +Category | Process +Sub-Category | Procedural +Created | 2022-04-19 +Dependencies | None + +# 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. +We also point out some problems that we are considering in this draft. Feel free to comment. + +# Specification + +All methods can be called via `window.chia`, such as + +```tsx +interface RequestArguments { + method: string; + params?: object; +} +window.chia.request(args: RequestArguments): Promise; +``` + +## Methods + +### accounts +To get the user's identity and check the dApp accessible. API returns the standard pay puzzleHash if dApp is approved. Otherwise, it returns the empty list. For the single address wallets, API returns one puzzleHash. For the multiple address wallet, API returns a list of puzzleHash. + +```tsx +accounts(): string[] +``` + +> Need discussion: +> +> Is it better to return the public key instead of puzzleHash? + +### chainId + +Return the current chainID, which is a hexadecimal string. + +| CHAIN NAME | CHAINID | +|------------|---------| +| Mainnet | 0x01 | +| Testnet10 | 0x02 | + +```tsx +chainId(): string +``` + +### requestAccounts + +dApps request the access. API returns the same output as `accounts` if users approve. Otherwise, API throws `UserRejectError.` + +```tsx +requestAccounts(): string[] +``` + +### walletSwitchChain + +dApps request to switch to another chain + +```tsx +walletSwitchChain(params: {chainId: string}): void +``` + +### transfer +dApps transfer asset. The wallet will handle the tx fee. + +Parameters as described below. +| Parameter | Description | +|-----------|------------------------------------------------------------------------------| +| to | bech32-encoded address with network prefix | +| amount | the transfer amount, unit is `mojo` | +| assetId | the asset id in the CAT1 standard. It is '' when transferring native tokens. | +| memos | hex string array | + +```tsx +interface TransferParams { + to: string; + amount: number; + assetId: string; + memos?: string[]; +} + +interface TransferResp { + transaction_id: string; + transaction: SpendBundle; +} + +transfer(params: TransferParams): TransferResp +``` + +### takeOffer +dApps take `offer.` The wallet will handle the tx fee. + +Parameters as described below. +| Parameter | Description | +|-----------|-------------------------------| +| offer | bech32-encoded `offer` string | + +```tsx +interface TakeOfferResp { + transaction_id: string; + transaction: SpendBundle; +} + +takeOffer(params: {offer: string}): TakeOfferResp +``` + +### createOffer +dApps create `offer,` which returns the bech32-encoded `offer` string. + +| Parameter | Description | +|---------------|------------------------------------------------------------------| +| offer | the assets the user will pay | +| requestAssets | the assets the user wants | +| fee | This is optional. If it is none, the wallet will handle the fee. | + +```tsx +interface AssetAmount { + assetId: string; + amount: string|number; +} + +interface createOfferParams { + offerAssets: AssetAmount[]; + requestAssets: AssetAmount[]; + fee?: string|number; +} + +interface createOfferResp { + transaction_id: string; + offer: string; +} + +createOffer(params: createOfferParams): createOfferResp +``` + +### selectAssetCoins +API returns the spendable coin amount for the selected CAT. + +Parameters as described below. +| Parameter | Description | +|-----------|--------------------------------------------------------------------| +| assetId | The CAT1 asset id. It will be '' if the user selects native tokens | +| amount | This is optional. If it is `None`, all the coins will be returned. | +| excludes | This is optional. This field is the `name` of coins. | + +```tsx +interface SelectAssetCoinsParams { + assetId: string; + amount?: number|string; + excludes?: string[]; +} + +interface Coin { + parent_coin_info: string; + puzzle_hash: string; + amount: number; +} + +interface selectAssetCoinsResp { + coins: Coin[] +} + +selectAssetCoins(params: SelectAssetCoinsParams): selectAssetCoinsResp +``` + +### createAssetTx +> Please note that, the API will be deprecated once the `signTx` is implemented. + +A limited and safe implemention of `signTx` so that the dApp can safely create a transferring-asset spend bundle and aggerate with a custom spend bundle. All the coins in the spend bundle will be tied together with `ASSERT_COIN_ANNOUNCEMENT.` + +Parameters as described below. +| Parameter | Description | +|--------------------|-------------------------------------------------------------------------------------| +| payments | The payment info | +| payment.puzzleHash | The target puzzleHash of payment | +| payment.amount | The amount of payment, unit is `mojo` | +| payment.memos | The memos of payment, the value is a list of hex strings. | +| fee | The field is valid when `assetId` is an empty string. | +| coins | The coins will be used in the spend bundle. | +| assetId | The asset id in the CAT1 standard. It will be '' if the user selects native tokens. | +| delta | `coinsAmount - (paymentAmount + fee - delta) = change` | +| tail | The field is the limitation program of `cat.` | +| tailSolution | The field is the solution to the limitation program of `cat` | + +> If the delta is positive, the extra spend bundle should offer more amount. If the value is negative, some amount is left, and the extra spend bundle can use it. + +```tsx +interface Payment { + puzzleHash: string; + amount: number|string; + memos?: string[]; +} + +interface CreateAssetTxItem { + payments: Payment[]; + fee?: number|string; + coins?: Coin[]; + assetId: string; + coinAnnouncements?: string[]; + coinAnnouncementsToAssert?: string[]; + puzzleAnnouncements?: string[]; + puzzleAnnouncementsToAssert?: string[]; + relativeSecondsToAssert?: number; + absoluteSecondsToAssert?: number; + relativeHeightToAssert?: number; + absoluteheightToAssert?: number; + delta?: number; + tail?: string; + tailSolution?: string; +} + +interface createAssetTxParams { + items: CreateAssetTxItem[] +} + +interface CreateAssetTxResp { + transaction_id: string; + transaction: SpendBundle; +} + +createAssetTx(params: createAssetTxParams): CreateAssetTxResp +``` + +### signTx +This is a lower-level API that signs custom coin spends. + +Parameters as described below. +| Parameter | Description | +|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------| +| coinSpends | a list of `coinSpend` | +| PubKeyHints | The hints for the public key in the `AGG_SIG_ME` and `AGG_SIG_UNSAFE` | +| PubKeyHint.pubKey | The public Key | +| PubKeyHint.hdPath | The HD path in the form' m/12381/8444/n/n', indices ending in an 'n' indicate that non-observer derivation should be used at that index. | +| PubKeyHint.hiddenPuzzleHash | If this value is not None, it means the pub key is a `synthetic public key` which is the sum of pub key from hdPath and the public key from hiddenPuzzleHash. | + +> Need discussion: +> +> whether support sign `AGG_SIG_UNSAFE` +> +> whether support BLS multisign? +> +> whether support sign ephemeral coin (not on the chain)? +> +> will there be the signature abuse? +> +> how to keep compatible with hardware wallets + +```tsx +interface CoinSpend { + coin: Coin; + puzzle_reveal: string; + solution: string; +} + +interface PubKeyHint { + pubKey: string; + hdPath: string; + hiddenPuzzleHash?: string; +} + +interface SignTxParams { + coinSpends: CoinSpend[] + pubKeyHints: PubKeyHint[] +} + +interface SignTxResp { + transaction_id: string; + transaction: SpendBundle; +} + +signTx(params: SignTxParams): SignTxResp +``` + +### signMessage +Sign the message encoded as a hex string using the private key associated with the account or the HD path's private key. + +Parameters as described below. +| Parameter | Description | +|-----------|-------------------------------------------------------------------------------------| +| message | the hex string needs to be signed | +| account | the data from method `accounts` | +| hdPath | The hdPath of the private key. If it's not None, the account field will be ignored. | + +```tsx +interface SignMessageParams { + message: string; + account?: string; + hdPath?: string; +} + +interface SignMessageResp { + pubKey: string; + signature: string; +} + +signMessage(params: SignMessageParams): SignMessageResp +``` + +> Need discussion: +> how to support BLS multi-sig? +> how to avoid sign tx data? + +### pushTx + +Even if the wallet supports `pushTx,` we still highly recommend that the dApp uses its full node to broadcast transactions. + +```tsx +interface PushTxParams { + spendBundle: SpendBundle; +} + +interface PushTxResp { + // mempool status, success/pending/failed + status: string; +} + +pushTx(params: PushTxParams): PushTxResp +``` + +> Need discussion: +> +> should the wallet track this kind of tx? + +### getPublicKey +Return the public key derived from hdPath + +Parameters as described below. +| Parameter | Description | +|-----------|--------------------------| +| hdPath | the hdPath of public key | + +```tsx +interface getPublicKeyParams { + hdPath: string; +} + +getPublicKey(params: getPublicKeyParams): string; +``` + +> Need discussion: +> dApp can get all the public keys, which may result in privacy leaks. Is it necessary to add an approval step? + +## Events + +### chainChanged + +the bridge emits `chainChanged` when connecting to a new chain + +```tsx +chia.on('chainChanged', listener: (chainId: string) => void) +``` + +### accountsChanged + +the bridge emits `accountsChanged` if the accounts change account in the wallet + +```tsx +chia.on('accountsChanged', listener: (accounts: string[]) => void) +``` + +## 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' +} +``` + +## Implementation + +The [Goby](https://goby.app) team has implemented the methods described above. + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).