Skip to content

Commit

Permalink
Merge pull request #147 from bandprotocol/feat/integrate-cosmostation…
Browse files Browse the repository at this point in the history
…-wallet

Integrate cosmostation wallet
  • Loading branch information
babybunn authored Jul 8, 2024
2 parents 642cc55 + 8bbf899 commit e2e1267
Show file tree
Hide file tree
Showing 16 changed files with 250 additions and 129 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
"@apollo/client": "^3.6.9",
"@bandprotocol/bandchain.js": "^2.1.4",
"@cosmostation/cosmosjs": "0.5.5",
"@cosmostation/extension-client": "^0.1.15",
"@glennsl/rescript-json-combinators": "^1.0.0",
"@ledgerhq/hw-transport-webhid": "^6.4.1",
"@ledgerhq/hw-transport-webusb": "^6.3.0",
Expand Down
201 changes: 83 additions & 118 deletions src/bindings/Cosmostation.res
Original file line number Diff line number Diff line change
@@ -1,134 +1,99 @@
module Cosmos = {
type requestAccountResponse = {
address: string,
publicKey: array<int>,
name: string,
isLedger: bool,
}
type t = {}

type amount = {
denom: string,
amount: string,
}

type fee = {
amount: array<amount>,
gas: string,
}

type signAminoDoc = {
chain_id: string,
sequence: string,
account_number: string,
fee: fee,
memo: string,
msgs: array<Js.Json.t>,
}

type signAminoResponse = {
signature: string,
pub_key: {"type": string, "value": string},
signed_doc: signAminoDoc,
}

type signDirectDoc = {
chain_id: string,
body_bytes: array<int>,
auth_info_bytes: array<int>,
account_number: string,
}

type signDirectResponse = {
signature: string,
pub_key: {"type": string, "value": string},
signed_doc: signDirectDoc,
}

type gasRateType = {
tiny: string,
low: string,
average: string,
}
type amount = {
denom: string,
amount: string,
}

type signOptions = {
memo?: bool,
fee?: bool,
gasRate?: gasRateType,
}
type fee = {
amount: array<amount>,
gas: string,
}

type supportedChainNamesResponse = {
official: array<string>,
unofficial: array<string>,
}
type sign_amino_doc_t = {
chain_id: string,
sequence: string,
account_number: string,
fee: fee,
memo: string,
msgs: array<Js.Json.t>,
}

type addChainParams = {
chainId: string,
chainName: string,
restURL: string,
imageURL?: string,
baseDenom: string,
displayDenom: string,
decimals?: float,
coinType?: string,
addressPrefix: string,
coinGeckoId?: string,
gasRate?: gasRateType,
sendGas?: string,
}
type sign_amino_param_t = {
chainName: string,
doc: option<sign_amino_doc_t>,
isEditFee: option<bool>,
isEditMemo: option<bool>,
}

type t = {
getAccount: string => Js.Promise.t<requestAccountResponse>,
signDirect: (string, signDirectDoc, option<signOptions>) => Js.Promise.t<signDirectResponse>,
signAmino: (string, signAminoDoc, option<signOptions>) => Js.Promise.t<signAminoResponse>,
}
type request_account_response_t = {
address: string,
publicKey: array<int>,
name: string,
isLedger: bool,
isEthermint: bool,
}

@module("@cosmostation/extension-client/cosmos")
external getSupportedChains: string => Js.Promise.t<supportedChainNamesResponse> =
"getSupportedChains"
type sign_amino_response_t = {
signature: string,
pub_key: {"type": string, "value": string},
signed_doc: sign_amino_doc_t,
}

@module("@cosmostation/extension-client/cosmos")
external getActivatedChains: string => Js.Promise.t<array<string>> = "getActivatedChains"
type request_t = {
method: string,
params: sign_amino_param_t,
}

@module("@cosmostation/extension-client/cosmos")
external addChain: addChainParams => Js.Promise.t<array<string>> = "addChain"
type sign_response_t = {signature: string}

@module("@cosmostation/extension-client/cosmos")
external getAccount: string => Js.Promise.t<requestAccountResponse> = "getAccount"
@val @scope("window")
external cosmostation: option<t> = "cosmostation"

@module("@cosmostation/extension-client/cosmos")
external signDirect: (
string,
signDirectDoc,
option<signOptions>,
) => Js.Promise.t<signDirectResponse> = "signDirect"
@val @scope(("window", "cosmostation", "cosmos"))
external request: 'a => Js.Promise.t<'b> = "request"

@module("@cosmostation/extension-client/cosmos")
external signAmino: (
string,
signAminoDoc,
option<signOptions>,
) => Js.Promise.t<signAminoResponse> = "signAmino"
let requestAccount = async (chainName: string) => {
let acc: request_account_response_t = await request({
method: "cos_requestAccount",
params: {
chainName,
doc: None,
isEditFee: None,
isEditMemo: None,
},
})

@module("@cosmostation/extension-client/cosmos")
external disconnect: unit => Js.Promise.t<unit> = "disconnect"
acc
}

let getAminoSignDocFromTx = (tx: BandChainJS.Transaction.transaction_t) => {
account_number: tx.accountNum->Belt.Option.getExn->Belt.Int.toString,
chain_id: tx.chainId->Belt.Option.getExn,
fee: {
amount: tx.fee
->BandChainJS.Fee.getAmountList
->Belt.Array.map(coin => {
amount: coin->BandChainJS.Coin.getAmount,
denom: coin->BandChainJS.Coin.getDenom,
}),
gas: tx.fee->BandChainJS.Fee.getGasLimit->Belt.Int.toString,
let signAmino = async (chainName: string, doc: sign_amino_doc_t) => {
let response: sign_amino_response_t = await request({
method: "cos_signAmino",
params: {
chainName,
doc: Some(doc),
isEditFee: Some(false),
isEditMemo: Some(false),
},
memo: tx.memo,
msgs: tx.msgs->Belt.Array.map(msg => msg->BandChainJS.Message.MsgSend.toJSON),
sequence: tx.sequence->Belt.Option.getExn->Belt.Int.toString,
}
})

response
}

@module("@cosmostation/extension-client")
external cosmos: unit => Js.Promise.t<Cosmos.t> = "cosmos"
let getAminoSignDocFromTx = (tx: BandChainJS.Transaction.transaction_t) => {
account_number: tx.accountNum->Belt.Option.getExn->Belt.Int.toString,
chain_id: tx.chainId->Belt.Option.getExn,
fee: {
amount: tx.fee
->BandChainJS.Fee.getAmountList
->Belt.Array.map(coin => {
amount: coin->BandChainJS.Coin.getAmount,
denom: coin->BandChainJS.Coin.getDenom,
}),
gas: tx.fee->BandChainJS.Fee.getGasLimit->Belt.Int.toString,
},
memo: tx.memo,
msgs: tx.msgs->Belt.Array.map(msg => msg->BandChainJS.Message.MsgSend.toJSON),
sequence: tx.sequence->Belt.Option.getExn->Belt.Int.toString,
}
4 changes: 3 additions & 1 deletion src/components/layout/UserAccount.res
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ module ConnectBtn = {
@react.component
let make = () => {
let (accountOpt, dispatchAccount) = React.useContext(AccountContext.context)
let (accountBoxState, setAccountBoxState) = React.useContext(WalletPopupContext.context)
let (accountBoxState, setAccountBoxState, _, _) = React.useContext(WalletPopupContext.context)
let trackingSub = TrackingSub.use()
let ({ThemeContext.theme: theme, isDarkMode}, _) = React.useContext(ThemeContext.context)

Expand Down Expand Up @@ -99,6 +99,8 @@ let make = () => {
| "leapBandNotfound" => <LeapBandNotfound chainID />
| "keplrNotfound" => <KeplrNotfound />
| "keplrBandNotfound" => <KeplrBandNotfound />
| "cosmostationNotfound" => <CosmostationNotfound />
| "error" => <ErrorConnection />
| "connectMnemonic" => <ConnectMnemonic chainID />
| "connectLedger" => <ConnectLedger chainID />
| _ => React.null
Expand Down
2 changes: 1 addition & 1 deletion src/components/page/oracle-script/OracleScriptExecute.res
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ module ExecutionPart = {

let (accountOpt, dispatch) = React.useContext(AccountContext.context)
let trackingSub = TrackingSub.use()
let (_, setAccountBoxState) = React.useContext(WalletPopupContext.context)
let (_, setAccountBoxState, _, _) = React.useContext(WalletPopupContext.context)

let connect = () => setAccountBoxState(_ => "noShow")
let numParams = paramsInput->Belt.Array.length
Expand Down
2 changes: 1 addition & 1 deletion src/components/page/validator/ValidatorStakingInfo.res
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ let make = (~validatorAddress) => {
let trackingSub = TrackingSub.use()
let (accountOpt, _) = React.useContext(AccountContext.context)
let ({ThemeContext.theme: theme, isDarkMode}, _) = React.useContext(ThemeContext.context)
let (_, setAccountBoxState) = React.useContext(WalletPopupContext.context)
let (_, setAccountBoxState, _, _) = React.useContext(WalletPopupContext.context)

let connect = () => setAccountBoxState(_ => "noShow")

Expand Down
2 changes: 1 addition & 1 deletion src/components/wallet/AccountBox.res
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ let make = () => {
let (accountOpt, dispatchAccount) = React.useContext(AccountContext.context)
let trackingSub = TrackingSub.use()
let (accountOpt, dispatchAccount) = React.useContext(AccountContext.context)
let (_, setAccountBoxState) = React.useContext(WalletPopupContext.context)
let (_, setAccountBoxState, _, _) = React.useContext(WalletPopupContext.context)

let send = () => {
SubmitMsg.Send(None, IBCConnectionQuery.BAND)->SubmitTx->OpenModal->dispatchModal
Expand Down
2 changes: 1 addition & 1 deletion src/components/wallet/ConnectLedger.res
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ let make = (~chainID) => {
let (accountIndex, setAccountIndex) = React.useState(_ => "0")
let (errMsg, setErrMsg) = React.useState(_ => "")
let (showAdvance, setShowAdvance) = React.useState(_ => false)
let (_, setAccountBoxState) = React.useContext(WalletPopupContext.context)
let (_, setAccountBoxState, _, _) = React.useContext(WalletPopupContext.context)

let ({ThemeContext.theme: theme, isDarkMode}, _) = React.useContext(ThemeContext.context)

Expand Down
2 changes: 1 addition & 1 deletion src/components/wallet/ConnectMnemonic.res
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ let make = (~chainID) => {
let (_, dispatchAccount) = React.useContext(AccountContext.context)
let (mnemonic, setMnemonic) = React.useState(_ => "")
let (errMsg, setErrMsg) = React.useState(_ => "")
let (_, setAccountBoxState) = React.useContext(WalletPopupContext.context)
let (_, setAccountBoxState, _, _) = React.useContext(WalletPopupContext.context)

let ({ThemeContext.theme: theme, isDarkMode}, _) = React.useContext(ThemeContext.context)

Expand Down
38 changes: 38 additions & 0 deletions src/components/wallet/CosmostationNotfound.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module Styles = {
open CssJs

let container = (theme: Theme.t, isDarkMode) =>
style(. [
display(#flex),
flexDirection(#column),
justifyContent(#center),
alignItems(#center),
position(#relative),
])

let icon = {
style(. [width(#px(48)), height(#px(48))])
}
}

@react.component
let make = () => {
let ({ThemeContext.theme: theme, isDarkMode}, _) = React.useContext(ThemeContext.context)

<div className={Styles.container(theme, isDarkMode)}>
<img alt={`$wallet icon`} src={Images.cosmostation} className=Styles.icon />
<VSpacing size={#px(8)} />
<Heading size={H2} value="Cosmostation is not installed" />
<VSpacing size={#px(8)} />
<Text
size={Body2}
align={Center}
value="If you have Cosmostation installed, refresh this page or follow your browser's instructions to connect your wallet."
/>
<VSpacing size={#px(24)} />
<LinkButton
href="https://cosmostation.io/products/cosmostation_extension" fullWidth=true fsize=16>
{"Install Cosmostation"->React.string}
</LinkButton>
</div>
}
30 changes: 30 additions & 0 deletions src/components/wallet/ErrorConnection.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Styles = {
open CssJs

let container = (theme: Theme.t, isDarkMode) =>
style(. [
display(#flex),
flexDirection(#column),
justifyContent(#center),
alignItems(#center),
position(#relative),
])

let icon = {
style(. [width(#px(48)), height(#px(48))])
}
}

@react.component
let make = () => {
let ({ThemeContext.theme: theme, isDarkMode}, _) = React.useContext(ThemeContext.context)
let (_, _, accountError, _) = React.useContext(WalletPopupContext.context)

<div className={Styles.container(theme, isDarkMode)}>
<img alt="cosmostation icon" src={Images.fail} className=Styles.icon />
<VSpacing size={#px(8)} />
<Heading size={H2} value="Something Wrong" />
<VSpacing size={#px(8)} />
<Text size={Body2} align={Center} value=accountError />
</div>
}
Loading

0 comments on commit e2e1267

Please sign in to comment.