From 206256073a9edca79c75f7219b290ca2762ae6de Mon Sep 17 00:00:00 2001 From: babybunn Date: Tue, 16 May 2023 18:17:12 +0700 Subject: [PATCH 1/5] feat: patch obi decode typesafe --- .../msg/tx-index/RenderMsgDetails.res | 177 +-- .../oracle-script/OracleScriptBridgeCode.res | 8 +- .../oracle-script/OracleScriptExecute.res | 19 +- .../OracleScriptExecuteResponse.res | 2 +- src/pages/RequestDetailsPage.res | 1001 ++++++++--------- src/reusable/InfoMobileCard.res | 2 +- src/utils/Msg.res | 21 +- src/utils/Obi2.res | 322 +++++- src/utils/proof/NonEVMProof.res | 28 +- 9 files changed, 937 insertions(+), 643 deletions(-) diff --git a/src/components/msg/tx-index/RenderMsgDetails.res b/src/components/msg/tx-index/RenderMsgDetails.res index b8ff42c2..ccaec8f6 100644 --- a/src/components/msg/tx-index/RenderMsgDetails.res +++ b/src/components/msg/tx-index/RenderMsgDetails.res @@ -23,7 +23,7 @@ module Calldata = { data={calldata->JsBuffer.toHex(~with0x=false)} title="Copy as bytes" width=125 /> - {Obi.decode(schema, "input", calldata)->Belt.Option.mapWithDefault(failed, calldataKVs => + {Obi2.decode(schema, Obi2.Input, calldata)->Belt.Option.mapWithDefault(failed, calldataKVs => Belt.Array.map(({fieldName, fieldValue}) => [ KVTable.Value(fieldName), @@ -62,7 +62,7 @@ type content_t = { title: string, content: content_inner_t, order: int, - heading?: string + heading?: string, } let renderValue = v => { @@ -70,7 +70,7 @@ let renderValue = v => { | Address(address) => | ValidatorAddress(address) => - | PlainText(content) => + | PlainText(content) => | CoinList(amount) => | ID(element) => element | Calldata(schema, data) => @@ -120,7 +120,7 @@ let renderValue = v => { ->Belt.List.toArray ->React.array} - | Packet(packet) => + | Packet(packet) => | MultiSendInputList(inputs) => , firsts) => + let factory = (msg: Msg.Channel.RecvPacket.t<'a>, firsts) => firsts->Belt.Array.concat([ { title: "Signer", @@ -996,85 +996,90 @@ module RecvPacket = { ]) let success = (msg: Msg.Channel.RecvPacket.success_t) => - msg->factory(switch msg.packetData { - | Some(packetData) => switch packetData.packetDetail { + msg->factory( + switch msg.packetData { + | Some(packetData) => + switch packetData.packetDetail { | OracleRequestPacket(details) => [ - { - heading: "Packet Data", - title: "", - content: PlainText(""), - order: 12, - }, - { - title: "Request ID", - content: ID(), - order: 13, - }, - { - title: "Oracle Script", - content: ID(), - order: 14, - }, - { - title: "Prepare Gas", - content: PlainText(details.prepareGas->Belt.Int.toString), - order: 15, - }, - { - title: "Execute Gas", - content: PlainText(details.executeGas->Belt.Int.toString), - order: 16, - }, - { - title: "Calldata", - content: Calldata(details.schema, details.calldata), - order: 17, - }, - { - title: "Request Validator Count", - content: PlainText(details.askCount->Belt.Int.toString), - order: 18, - }, - { - title: "Sufficient Validator Count", - content: PlainText(details.minCount->Belt.Int.toString), - order: 19, - }, - ] + { + heading: "Packet Data", + title: "", + content: PlainText(""), + order: 12, + }, + { + title: "Request ID", + content: ID(), + order: 13, + }, + { + title: "Oracle Script", + content: ID( + , + ), + order: 14, + }, + { + title: "Prepare Gas", + content: PlainText(details.prepareGas->Belt.Int.toString), + order: 15, + }, + { + title: "Execute Gas", + content: PlainText(details.executeGas->Belt.Int.toString), + order: 16, + }, + { + title: "Calldata", + content: Calldata(details.schema, details.calldata), + order: 17, + }, + { + title: "Request Validator Count", + content: PlainText(details.askCount->Belt.Int.toString), + order: 18, + }, + { + title: "Sufficient Validator Count", + content: PlainText(details.minCount->Belt.Int.toString), + order: 19, + }, + ] | FungibleTokenPacket(details) => [ - { - heading: "Packet Data", - title: "", - content: PlainText(""), - order: 12, - }, - { - title: "Sender", - content: PlainText(details.sender), - order: 13, - }, - { - title: "Receiver", - content: PlainText(details.receiver), - order: 14, - }, - { - title: "Amount", - content: PlainText(details.amount->Belt.Int.toString), - order: 15, - }, - - ] + { + heading: "Packet Data", + title: "", + content: PlainText(""), + order: 12, + }, + { + title: "Sender", + content: PlainText(details.sender), + order: 13, + }, + { + title: "Receiver", + content: PlainText(details.receiver), + order: 14, + }, + { + title: "Amount", + content: PlainText(details.amount->Belt.Int.toString), + order: 15, + }, + ] | Unknown => [] - } + } | None => [] - }) + }, + ) let failed = (msg: Msg.Channel.RecvPacket.fail_t) => msg->factory([]) } module AcknowledgePacket = { - let factory = (msg: Msg.Channel.AcknowledgePacket.t) => {[ + let factory = (msg: Msg.Channel.AcknowledgePacket.t) => { + [ { title: "Signer", content: Address(msg.signer), @@ -1132,11 +1137,13 @@ module AcknowledgePacket = { content: Timestamp(msg.packet.timeoutTimestamp), order: 11, }, - ]} + ] + } } module Timeout = { - let factory = (msg: Msg.Channel.Timeout.t) => {[ + let factory = (msg: Msg.Channel.Timeout.t) => { + [ { title: "Signer", content: Address(msg.signer), @@ -1194,7 +1201,8 @@ module Timeout = { content: Timestamp(msg.packet.timeoutTimestamp), order: 11, }, - ]} + ] + } } module CreateClient = { @@ -1272,12 +1280,12 @@ module ConnectionOpenTry = { content: PlainText(msg.clientID), order: 2, }, - { + { title: "Delay Period", content: PlainText(msg.delayPeriod->Belt.Int.toString ++ "ns"), order: 3, }, - { + { title: "Previous Connection ID", content: PlainText(msg.previousConnectionID), order: 4, @@ -1461,7 +1469,6 @@ module ChannelOpenInit = { content: PlainText(msg.channel.counterparty.portID), order: 6, }, - ] } } @@ -1707,8 +1714,7 @@ module Transfer = { }, ]) - let failed = (msg: Msg.Application.Transfer.fail_t) => - msg->factory([]) + let failed = (msg: Msg.Application.Transfer.fail_t) => msg->factory([]) } let getContent = msg => { @@ -1787,9 +1793,10 @@ let getContent = msg => { | Msg.Gov.VoteWeighted.Failure(data) => VoteWeighted.failed(data) } | Msg.CreateClientMsg(data) => CreateClient.factory(data) - | Msg.UpdateClientMsg(data) + | Msg.UpdateClientMsg(data) | Msg.UpgradeClientMsg(data) - | Msg.SubmitClientMisbehaviourMsg(data) => Client.factory(data) + | Msg.SubmitClientMisbehaviourMsg(data) => + Client.factory(data) | Msg.RecvPacketMsg(m) => switch m { | Msg.Channel.RecvPacket.Success(data) => RecvPacket.success(data) diff --git a/src/components/oracle-script/OracleScriptBridgeCode.res b/src/components/oracle-script/OracleScriptBridgeCode.res index 2bdfaa08..20e7f071 100644 --- a/src/components/oracle-script/OracleScriptBridgeCode.res +++ b/src/components/oracle-script/OracleScriptBridgeCode.res @@ -140,7 +140,7 @@ module LanguageIcon = { } let getFileNameFromLanguage = (~language, ~dataType) => { - let dataTypeString = dataType->Obi.dataTypeToString + let dataTypeString = dataType->Obi2.dataTypeToString switch language { | Solidity => "Decoders.sol" | Go => j`$(dataTypeString)Decoder.go` @@ -149,8 +149,8 @@ let getFileNameFromLanguage = (~language, ~dataType) => { let getCodeFromSchema = (~schema, ~language, ~dataType) => { switch language { - | Solidity => Obi.generateDecoderSolidity(schema) - | Go => Obi.generateDecoderGo("main", schema, dataType) + | Solidity => Obi2.generateDecoderSolidity(schema) + | Go => Obi2.generateDecoderGo("main", schema, dataType) } } @@ -270,6 +270,6 @@ let make = (~schema) => {
{schema->renderCode}
- + } diff --git a/src/components/oracle-script/OracleScriptExecute.res b/src/components/oracle-script/OracleScriptExecute.res index d71b31e4..c872dba4 100644 --- a/src/components/oracle-script/OracleScriptExecute.res +++ b/src/components/oracle-script/OracleScriptExecute.res @@ -94,7 +94,7 @@ module ConnectPanel = { module ParameterInput = { @react.component - let make = (~params: Obi.field_key_type_t, ~index, ~setCallDataArr) => { + let make = (~params: Obi2.field_key_type_t, ~index, ~setCallDataArr) => { let fieldType = params.fieldType let fieldName = params.fieldName->Js.String2.replaceByRe(%re(`/[_]/g`), " ") let ({ThemeContext.theme: theme, isDarkMode}, _) = React.useContext(ThemeContext.context) @@ -285,7 +285,7 @@ module ExecutionPart = { let make = ( ~id: ID.OracleScript.t, ~schema: string, - ~paramsInput: array, + ~paramsInput: array, ) => { let isMobile = Media.isMobile() @@ -393,17 +393,16 @@ module ExecutionPart = { style={Styles.button(theme, result == Loading)} onClick={_ => if result !== Loading { - switch Obi.encode( - schema, - "input", + let inputDataEncode = paramsInput ->Belt.Array.map(({fieldName}) => fieldName) ->Belt.Array.zip(callDataArr) ->Belt.Array.map(((fieldName, fieldValue)) => { - open Obi + open Obi2 {fieldName, fieldValue} - }), - ) { + }) + + switch Obi2.encode(schema, Obi2.Input, inputDataEncode) { | Some(encoded) => setResult(_ => Loading) let _ = TxCreator.sendTransaction( @@ -426,7 +425,7 @@ module ExecutionPart = { feeLimit ->Belt.Float.fromString ->Belt.Option.getExn - ->Coin.newUBANDFromAmount + ->Coin.newUBANDFromAmount, }, prepareGas, executeGas, @@ -474,7 +473,7 @@ module ExecutionPart = { @react.component let make = (~id: ID.OracleScript.t, ~schema: string) => { - let paramsInput = schema->Obi.extractFields("input")->Belt.Option.getExn + let paramsInput = schema->Obi2.extractFields("input")->Belt.Option.getExn Some() }->Belt.Option.getWithDefault( diff --git a/src/components/oracle-script/OracleScriptExecuteResponse.res b/src/components/oracle-script/OracleScriptExecuteResponse.res index 13493a39..893a9090 100644 --- a/src/components/oracle-script/OracleScriptExecuteResponse.res +++ b/src/components/oracle-script/OracleScriptExecuteResponse.res @@ -52,7 +52,7 @@ let make = (~txResponse: TxCreator.tx_response_t, ~schema: string) => {switch requestOpt { | Some({resolveStatus: Success, result: Some(result), id}) => - let outputKVsOpt = Obi.decode(schema, "output", result) + let outputKVsOpt = Obi2.decode(schema, Obi2.Output, result) switch outputKVsOpt { | Some(outputKVs) => <> diff --git a/src/pages/RequestDetailsPage.res b/src/pages/RequestDetailsPage.res index 3b946d37..5aaf9915 100644 --- a/src/pages/RequestDetailsPage.res +++ b/src/pages/RequestDetailsPage.res @@ -8,21 +8,20 @@ module Styles = { backgroundColor(theme.neutral_100), borderRadius(#px(4)), boxShadow(Shadow.box(~x=#zero, ~y=#px(2), ~blur=#px(4), rgba(0, 0, 0, #num(0.1)))), - ]); - let linkToHome = style(. [display(#flex), alignItems(#center), cursor(#pointer)]); - let rightArrow = style(. [width(#px(20)), filter([#saturate(50.0), #brightness(70.0)])]); - let noDataImage = style(. [width(#auto), height(#px(70)), marginBottom(#px(16))]); - let kvTableContainer = style(. [padding(#px(1))]); - let kvTableHeader = - style(. [ - padding4(~top=#px(16), ~left=#px(24), ~right=#px(24), ~bottom=#px(12)), - Media.mobile([padding2(~v=#px(14), ~h=#px(12))]), - ]); + ]) + let linkToHome = style(. [display(#flex), alignItems(#center), cursor(#pointer)]) + let rightArrow = style(. [width(#px(20)), filter([#saturate(50.0), #brightness(70.0)])]) + let noDataImage = style(. [width(#auto), height(#px(70)), marginBottom(#px(16))]) + let kvTableContainer = style(. [padding(#px(1))]) + let kvTableHeader = style(. [ + padding4(~top=#px(16), ~left=#px(24), ~right=#px(24), ~bottom=#px(12)), + Media.mobile([padding2(~v=#px(14), ~h=#px(12))]), + ]) - let kvTableMobile = style(. [margin(#zero), boxShadow(#none)]); - let addressContainer = style(. [Media.mobile([width(#px(260))])]); - let validatorReportStatus = style(. [marginBottom(#px(13))]); - let noPaddingBottom = style(. [paddingBottom(#zero), Media.mobile([paddingBottom(#zero)])]); + let kvTableMobile = style(. [margin(#zero), boxShadow(#none)]) + let addressContainer = style(. [Media.mobile([width(#px(260))])]) + let validatorReportStatus = style(. [marginBottom(#px(13))]) + let noPaddingBottom = style(. [paddingBottom(#zero), Media.mobile([paddingBottom(#zero)])]) let ibcBadge = (theme: Theme.t) => style(. [ display(#flex), @@ -30,7 +29,7 @@ module Styles = { background(theme.primary_600), borderRadius(#px(10)), marginLeft(#px(8)), - ]); + ]) let reasonSection = (theme: Theme.t) => style(. [ padding2(~v=#px(24), ~h=#px(40)), @@ -39,72 +38,62 @@ module Styles = { marginTop(#px(40)), display(#flex), alignItems(#center), - ]); - let notFoundLogo = style(. [width(#px(120))]); -}; + ]) + let notFoundLogo = style(. [width(#px(120))]) +} module ValidatorReportStatus = { @react.component let make = (~moniker, ~isReport, ~resolveStatus, ~operatorAddress) => { - let ({ThemeContext.theme}, _) = React.useContext(ThemeContext.context); + let ({ThemeContext.theme: theme}, _) = React.useContext(ThemeContext.context)
{switch (isReport, resolveStatus) { - | (true, _) => - | (false, _) => - }} + | (true, _) => + | (false, _) => + }} -
; - }; -}; + + } +} module KVTableContainer = { module TableHeader = { @react.component let make = () => { - let ({ThemeContext.theme}, _) = React.useContext(ThemeContext.context); + let ({ThemeContext.theme: theme}, _) = React.useContext(ThemeContext.context) - + - ; - }; - }; + + } + } module Loading = { @react.component let make = () => { - let isMobile = Media.isMobile(); + let isMobile = Media.isMobile() isMobile ? @@ -112,61 +101,67 @@ module KVTableContainer = { - - + + + + + + - - + + + + + + - ; - }; - }; + + } + } @react.component let make = (~decodesOpt) => { - let isMobile = Media.isMobile(); - let ({ThemeContext.theme, isDarkMode}, _) = React.useContext(ThemeContext.context); + let isMobile = Media.isMobile() + let ({ThemeContext.theme: theme, isDarkMode}, _) = React.useContext(ThemeContext.context) - switch (decodesOpt) { + switch decodesOpt { | Some(decodes) => isMobile ? decodes - ->Belt.Array.map(({Obi.fieldName, fieldValue}) => - - ) + ->Belt.Array.map(({Obi2.fieldName: fieldName, fieldValue}) => + + ) ->React.array : <> {decodes - ->Belt.Array.map(({Obi.fieldName, fieldValue}) => { - - - - - - - - - - - }) - ->React.array} + ->Belt.Array.map(({Obi2.fieldName: fieldName, fieldValue}) => { + + + + + + + + + + + }) + ->React.array} | None => @@ -183,18 +178,18 @@ module KVTableContainer = { color={theme.neutral_600} /> - }; - }; -}; + } + } +} @react.component let make = (~reqID) => { - let requestSub = RequestSub.get(reqID); - let isMobile = Media.isMobile(); + let requestSub = RequestSub.get(reqID) + let isMobile = Media.isMobile() - let ({ThemeContext.theme, isDarkMode}, _) = React.useContext(ThemeContext.context); + let ({ThemeContext.theme: theme, isDarkMode}, _) = React.useContext(ThemeContext.context) - switch (requestSub) { + switch requestSub { | NoData | Error(_) =>
@@ -217,10 +212,7 @@ let make = (~reqID) => { Right Arrow Icon @@ -235,18 +227,18 @@ let make = (~reqID) => { - {switch (requestSub) { - | Data({id, isIBC}) => -
- - {isIBC - ?
- -
- : React.null} -
- | _ => - }} + {switch requestSub { + | Data({id, isIBC}) => +
+ + {isIBC + ?
+ +
+ : React.null} +
+ | _ => + }}
@@ -264,87 +256,77 @@ let make = (~reqID) => { /> - {switch (requestSub) { - | Data({oracleScript: {oracleScriptID, name}}) => -
- - - -
- | _ => - }} + {switch requestSub { + | Data({oracleScript: {oracleScriptID, name}}) => +
+ + + +
+ | _ => + }}
- {switch (requestSub) { - | Data({requester}) => - switch (requester) { - | Some(requester') => -
- -
- | None => - } - | _ => - }} + {switch requestSub { + | Data({requester}) => + switch requester { + | Some(requester') => +
+ +
+ | None => + } + | _ => + }}
- {switch (requestSub) { - | Data({transactionOpt}) => - switch (transactionOpt) { - | Some({hash}) => - | None => - } - | _ => - }} + {switch requestSub { + | Data({transactionOpt}) => + switch transactionOpt { + | Some({hash}) => + | None => + } + | _ => + }} - {switch (requestSub) { - | Data({transactionOpt}) => - switch (transactionOpt) { - | Some({gasFee}) => - Coin.getBandAmountFromCoins -> Format.fPretty(~digits=6)) - ++ " BAND" - } - size=Text.Body1 - color={theme.neutral_600} - /> - | None => - } - | _ => - }} + {switch requestSub { + | Data({transactionOpt}) => + switch transactionOpt { + | Some({gasFee}) => + Coin.getBandAmountFromCoins + ->Format.fPretty(~digits=6) ++ " BAND"} + size=Text.Body1 + color={theme.neutral_600} + /> + | None => + } + | _ => + }} @@ -357,16 +339,16 @@ let make = (~reqID) => { /> - {switch (requestSub) { - | Data({prepareGas}) => - Belt.Int.toString} - size=Text.Body1 - color={theme.neutral_600} - /> - | _ => - }} + {switch requestSub { + | Data({prepareGas}) => + Belt.Int.toString} + size=Text.Body1 + color={theme.neutral_600} + /> + | _ => + }} @@ -379,41 +361,37 @@ let make = (~reqID) => { /> - {switch (requestSub) { - | Data({executeGas}) => - Belt.Int.toString} - size=Text.Body1 - color={theme.neutral_600} - /> - | _ => - }} + {switch requestSub { + | Data({executeGas}) => + Belt.Int.toString} + size=Text.Body1 + color={theme.neutral_600} + /> + | _ => + }} - {switch (requestSub) { - | Data({feeLimit}) => - Coin.getBandAmountFromCoins -> Format.fPretty(~digits=6)) - ++ " BAND" - } - size=Text.Body1 - color={theme.neutral_600} - /> - | _ => - }} + {switch requestSub { + | Data({feeLimit}) => + Coin.getBandAmountFromCoins + ->Format.fPretty(~digits=6) ++ " BAND"} + size=Text.Body1 + color={theme.neutral_600} + /> + | _ => + }} @@ -426,23 +404,21 @@ let make = (~reqID) => { /> - {switch (requestSub) { - | Data({feeUsed, feeLimit}) => - let feeUsed_ = feeUsed -> Coin.getBandAmountFromCoins; - let feeLimit_ = feeLimit -> Coin.getBandAmountFromCoins; - let usedRatio = - ((feeLimit_ == 0. ? 0. : feeUsed_ /. feeLimit_) *. 100.) - -> Format.fPercent(~digits=2) - Format.fPretty(~digits=6)) ++ " BAND " ++ {j`($usedRatio)`} - } - size=Text.Body1 - color={theme.neutral_600} - />; - | _ => - }} + {switch requestSub { + | Data({feeUsed, feeLimit}) => + let feeUsed_ = feeUsed->Coin.getBandAmountFromCoins + let feeLimit_ = feeLimit->Coin.getBandAmountFromCoins + let usedRatio = + ((feeLimit_ == 0. ? 0. : feeUsed_ /. feeLimit_) *. 100.) + ->Format.fPercent(~digits=2) + Format.fPretty(~digits=6) ++ " BAND " ++ {j`($usedRatio)`}} + size=Text.Body1 + color={theme.neutral_600} + /> + | _ => + }} @@ -456,14 +432,14 @@ let make = (~reqID) => { /> - {switch (requestSub) { - | Data({transactionOpt}) => - switch (transactionOpt) { - | Some({blockHeight}) => - | None => - } - | _ => - }} + {switch requestSub { + | Data({transactionOpt}) => + switch transactionOpt { + | Some({blockHeight}) => + | None => + } + | _ => + }} @@ -476,15 +452,15 @@ let make = (~reqID) => { /> - {switch (requestSub) { - | Data({minCount, requestedValidators, reports}) => - Belt.Array.size} - minimumValidators=minCount - requestValidators={requestedValidators->Belt.Array.size} - /> - | _ => - }} + {switch requestSub { + | Data({minCount, requestedValidators, reports}) => + Belt.Array.size} + minimumValidators=minCount + requestValidators={requestedValidators->Belt.Array.size} + /> + | _ => + }} @@ -497,77 +473,70 @@ let make = (~reqID) => { /> - {switch (requestSub) { - | Data({resolveStatus, resolveHeight}) => -
- - {switch (resolveHeight) { - | Some(height) => - <> - - - - - - | None => React.null - }} -
- | _ => - }} + {switch requestSub { + | Data({resolveStatus, resolveHeight}) => +
+ + {switch resolveHeight { + | Some(height) => + <> + + + + + + | None => React.null + }} +
+ | _ => + }}
- {switch (requestSub) { - | Data({requestedValidators, resolveStatus, reports}) => - requestedValidators - ->Belt.Array.map( - ({validator: {moniker, consensusAddress, operatorAddress}}) => { - let isReport = - reports->Belt.Array.some(({reportValidator}) => - consensusAddress == reportValidator.consensusAddress - ); - - - ; - }) - ->React.array - | _ => - - - - - }} + {switch requestSub { + | Data({requestedValidators, resolveStatus, reports}) => + requestedValidators + ->Belt.Array.map(({ + validator: {moniker, consensusAddress, operatorAddress}, + }) => { + let isReport = + reports->Belt.Array.some(({reportValidator}) => + consensusAddress == reportValidator.consensusAddress + ) + + + + }) + ->React.array + | _ => + + + + + }} - {switch (requestSub) { - | Data({reason}) => - switch (reason) { - | Some(reason') when reason' !== "" => -
- Fail Icon - - -
- | _ => React.null - } - | _ => React.null - }} + {switch requestSub { + | Data({reason}) => + switch reason { + | Some(reason') if reason' !== "" => +
+ Fail Icon + + +
+ | _ => React.null + } + | _ => React.null + }} @@ -577,24 +546,22 @@ let make = (~reqID) => {
- {switch (requestSub) { - | Data({calldata}) => - JsBuffer.toHex(~with0x=false)} - title="Copy as bytes" - width=125 - /> - | _ => - }} + {switch requestSub { + | Data({calldata}) => + JsBuffer.toHex(~with0x=false)} title="Copy as bytes" width=125 + /> + | _ => + }}
- {switch (requestSub) { - | Data({oracleScript: {schema}, calldata}) => - let decodesOpt = Obi.decode(schema, "input", calldata); - ; - | _ => - }} + {switch requestSub { + | Data({oracleScript: {schema}, calldata}) => + let decodesOpt = Obi2.decode(schema, Obi2.Input, calldata) + + | _ => + }}
@@ -605,56 +572,54 @@ let make = (~reqID) => {
- {switch (requestSub) { - | Data({result: resultOpt, resolveStatus}) => - switch (resultOpt, resolveStatus) { - | (Some(result), Success) => - JsBuffer.toHex(~with0x=false)} - title="Copy as bytes" - width=125 - /> - | (_, _) => React.null - } - | _ => - }} + {switch requestSub { + | Data({result: resultOpt, resolveStatus}) => + switch (resultOpt, resolveStatus) { + | (Some(result), Success) => + JsBuffer.toHex(~with0x=false)} title="Copy as bytes" width=125 + /> + | (_, _) => React.null + } + | _ => + }}
- {switch (requestSub) { - | Data({oracleScript: {schema}, result: resultOpt, resolveStatus}) => - switch (resolveStatus, resultOpt) { - | (RequestSub.Success, Some(result)) => - let decodesOpt = Obi.decode(schema, "output", result); - ; - | (Pending, _) => - - - - - | (_, _) => - - Not Found - - - } - | _ => - }} + {switch requestSub { + | Data({oracleScript: {schema}, result: resultOpt, resolveStatus}) => + switch (resolveStatus, resultOpt) { + | (RequestSub.Success, Some(result)) => + let decodesOpt = Obi2.decode(schema, Obi2.Output, result) + + | (Pending, _) => + + + + + | (_, _) => + + Not Found + + + } + | _ => + }}
@@ -674,39 +639,39 @@ let make = (~reqID) => { - {switch (requestSub) { - | Data(request) => - switch (request.resolveStatus) { - | Success => - | Pending => - - - - - | _ => - - Not Found - - - } - | _ => - }} + {switch requestSub { + | Data(request) => + switch request.resolveStatus { + | Success => + | Pending => + + + + + | _ => + + Not Found + + + } + | _ => + }} @@ -718,127 +683,135 @@ let make = (~reqID) => {
{isMobile - ? React.null - : - - - - - - - - - - - -
- -
- -
- } - {switch (requestSub) { - | Data({rawDataRequests}) => - rawDataRequests - ->Belt.Array.map( - ({externalID, fee, dataSource: {dataSourceID, name}, calldata}) => { - isMobile - ? JsBuffer.toUTF8)), - ]} - key={externalID ++ name} - idx={externalID ++ name} - styles=Styles.kvTableMobile - /> - : - - - - - -
- -
- - -
- - - -
- - -
- JsBuffer.toUTF8} - color={theme.neutral_600} - weight=Text.Thin - align=Text.Right - breakAll=true - /> -
- -
- - }) - ->React.array - | _ => - isMobile - ? + + + + + + + + + + + +
+ +
+ +
+ } + {switch requestSub { + | Data({rawDataRequests}) => + rawDataRequests + ->Belt.Array.map(({ + externalID, + fee, + dataSource: {dataSourceID, name}, + calldata, + }) => { + isMobile + ? JsBuffer.toUTF8)), + ] + } + key={externalID ++ name} + idx={externalID ++ name} + styles=Styles.kvTableMobile + /> + : + + + + + +
+ +
+ + +
+ + + +
+ + +
+ JsBuffer.toUTF8} + color={theme.neutral_600} + weight=Text.Thin + align=Text.Right + breakAll=true + /> +
+ +
+ + }) + ->React.array + | _ => + isMobile + ? - : - - - - - -
- -
- -
- - }} + ("External ID", Loading(60)), + ("Fee\n(BAND)", Loading(60)), + ("Data Source", Loading(60)), + ("Param", Loading(60)), + ] + } + idx="1" + styles=Styles.kvTableMobile + /> + : + + + + + + + + + + + +
+ +
+ +
+ + }}
@@ -850,15 +823,15 @@ let make = (~reqID) => {
- {switch (requestSub) { - | Data({reports}) => - | _ => - }} + {switch requestSub { + | Data({reports}) => + | _ => + }}
- }; -}; + } +} diff --git a/src/reusable/InfoMobileCard.res b/src/reusable/InfoMobileCard.res index 09b66bc6..5cc9dcb2 100644 --- a/src/reusable/InfoMobileCard.res +++ b/src/reusable/InfoMobileCard.res @@ -27,7 +27,7 @@ type t = | ProgressBar(request_count_t) | Float(float, option) | KVTableReport(array, array) - | KVTableRequest(option>) + | KVTableRequest(option>) | CopyButton(JsBuffer.t) | Percentage(float, option) | Timestamp(MomentRe.Moment.t) diff --git a/src/utils/Msg.res b/src/utils/Msg.res index 3aaa881f..dcf68ca9 100644 --- a/src/utils/Msg.res +++ b/src/utils/Msg.res @@ -889,7 +889,6 @@ module Client = { } } - module Height = { type t = { revisionHeight: int, @@ -926,13 +925,13 @@ module Packet = { destinationPort: json.required(list{"destination_port"}, string), destinationChannel: json.required(list{"destination_channel"}, string), timeoutHeight: json.required(list{"timeout_height", "revision_height"}, int), - timeoutTimestamp: json.required(list{"timeout_timestamp"}, GraphQLParser.timeNS), + timeoutTimestamp: json.required(list{"timeout_timestamp"}, timeNS), data: json.required(list{"data"}, string), }) } } -let getStateText = state => +let getStateText = state => switch state { | 0 => "Uninitialized" | 1 => "Init" @@ -942,7 +941,7 @@ let getStateText = state => | _ => "Unknown" } -let getOrderText = order => +let getOrderText = order => switch order { | 0 => "None" | 1 => "Unordered" @@ -1156,7 +1155,6 @@ module Channel = { } } - module Deposit = { type t<'a> = { depositor: Address.t, @@ -1219,7 +1217,7 @@ module Connection = { let decode = { open JsonUtils.Decode buildObject(json => { - signer: json.required(list{"msg", "signer"}, string) -> Address.fromBech32, + signer: json.required(list{"msg", "signer"}, string)->Address.fromBech32, clientID: json.required(list{"msg", "client_id"}, string), delayPeriod: json.required(list{"msg", "delay_period"}, int), counterparty: json.required(list{"msg", "counterparty"}, ConnectionCounterParty.decode), @@ -1288,7 +1286,6 @@ module Connection = { } } - module Activate = { type t = {validatorAddress: Address.t} let decode = { @@ -1308,7 +1305,7 @@ module Application = { sourceChannel: string, timeoutHeight: Height.t, timeoutTimestamp: MomentRe.Moment.t, - token: 'a + token: 'a, } type success_t = t @@ -1326,8 +1323,8 @@ module Application = { sourcePort: json.required(list{"msg", "source_port"}, string), sourceChannel: json.required(list{"msg", "source_channel"}, string), timeoutHeight: json.required(list{"msg", "timeout_height"}, Height.decode), - timeoutTimestamp: json.required(list{"msg", "timeout_timestamp"}, GraphQLParser.timeNS), - token: json->tokenD + timeoutTimestamp: json.required(list{"msg", "timeout_timestamp"}, timeNS), + token: json->tokenD, }) } @@ -1664,6 +1661,7 @@ let rec decodeMsg = (json, isSuccess) => { let msg = json->mustDecode(Gov.VoteWeighted.decodeFail) (VoteWeightedMsg(Failure(msg)), msg.voterAddress, false) } + | "/ibc.core.client.v1.MsgCreateClient" => let msg = json->mustDecode(Client.Create.decode) (CreateClientMsg(msg), msg.signer, true) @@ -1686,6 +1684,7 @@ let rec decodeMsg = (json, isSuccess) => { let msg = json->mustDecode(Channel.RecvPacket.decodeFail) (RecvPacketMsg(Failure(msg)), msg.signer, true) } + | "/ibc.core.channel.v1.MsgAcknowledgement" => let msg = json->mustDecode(Channel.AcknowledgePacket.decode) (AcknowledgePacketMsg(msg), msg.signer, true) @@ -1731,7 +1730,7 @@ let rec decodeMsg = (json, isSuccess) => { | "/ibc.applications.transfer.v1.MsgTransfer" => isSuccess ? { - let msg = json->mustDecode(Application.Transfer.decodeSuccess) + let msg = json->mustDecode(Application.Transfer.decodeSuccess) (TransferMsg(Success(msg)), msg.sender, true) } : { diff --git a/src/utils/Obi2.res b/src/utils/Obi2.res index 45c877d2..75166d4c 100644 --- a/src/utils/Obi2.res +++ b/src/utils/Obi2.res @@ -18,7 +18,22 @@ let flowToString = x => | Output => "output" } -// TODO +type data_type_t = + | Params + | Result + +let dataTypeToString = dataType => + switch dataType { + | Params => "Params" + | Result => "Result" + } + +let dataTypeToSchemaField = dataType => + switch dataType { + | Params => "input" + | Result => "output" + } + let extractFields: (string, string) => option> = %raw(` function(schema, t) { try { @@ -52,13 +67,12 @@ let encode = (schema, flow, valuePairs) => { valuePairs ->Belt.Array.keepMap(each => fieldName == each.fieldName ? Some(each.fieldValue) : None) ->Belt.Array.getExn(0) - Js.log(value) - // TODO: parse follow fieldType let parsed = value->Js.Json.parseExn (fieldName, parsed) }) let data = Js.Json.object_(Js.Dict.fromArray(dataPairs)) + let obi = create(schema) switch flow { | Input => obi->encodeInput(data) @@ -71,3 +85,305 @@ let encode = (schema, flow, valuePairs) => { | encoded => Some(encoded) } } + +let stringify: string => string = %raw(`function(data) { + if (Array.isArray(data)) { + return "[" + [...data].map(stringify).join(",") + "]" + } else if (typeof(data) === "bigint") { + return data.toString() + } else if (Buffer.isBuffer(data)) { + return "0x" + data.toString('hex') + } else if (typeof(data) === "object") { + return "{" + Object.entries(data).map(([k,v]) => JSON.stringify(k)+ ":" + stringify(v)).join(",") + "}" + } else { + return JSON.stringify(data) + } + } + `) + +let decode = (schema, flow, data) => { + open BandChainJS.Obi + + switch { + let obi = create(schema) + + let rawResult = switch flow { + | Input => obi->decodeInput(data)->Js.Dict.entries + | Output => obi->decodeOutput(data)->Js.Dict.entries + } + + rawResult->Belt.Array.map(((k, v)) => { + {fieldName: k, fieldValue: stringify(v)} + }) + } { + | exception err => + Js.Console.error({`Error decode`}) // For debug + None + | decoded => Some(decoded) + } +} + +type primitive_t = + | String + | U64 + | U32 + | U8 + +type variable_t = + | Single(primitive_t) + | Array(primitive_t) + +type field_t = { + name: string, + varType: variable_t, +} + +let parse = ({fieldName, fieldType}) => { + let v = { + switch fieldType { + | "string" => Some(Single(String)) + | "u64" => Some(Single(U64)) + | "u32" => Some(Single(U32)) + | "u8" => Some(Single(U8)) + | "[string]" => Some(Array(String)) + | "[u64]" => Some(Array(U64)) + | "[u32]" => Some(Array(U32)) + | "[u8]" => Some(Array(U8)) + | _ => None + } + } + + v->Belt.Option.map(varType' => {name: fieldName, varType: varType'}) +} + +let declarePrimitiveSol = primitive => + switch primitive { + | String => "string" + | U64 => "uint64" + | U32 => "uint32" + | U8 => "uint8" + } + +let declareSolidity = ({name, varType}) => { + let type_ = switch varType { + | Single(x) => declarePrimitiveSol(x) + | Array(x) => { + let declareType = declarePrimitiveSol(x) + `${declareType}[]` + } + } + `${type_} ${name};` +} + +let assignSolidity = ({name, varType}) => { + let decode = primitive => + switch primitive { + | String => "string(data.decodeBytes());" + | U64 => "data.decodeU64();" + | U32 => "data.decodeU32();" + | U8 => "data.decodeU8();" + } + + switch varType { + | Single(x) => { + let decodeFunction = decode(x) + `result.${name} = ${decodeFunction}` + } + + | Array(x) => { + let type_ = declarePrimitiveSol(x) + let decodeFunction = decode(x) + + `uint32 length = data.decodeU32(); + ${type_}[] memory ${name} = new ${type_}[](length); + for (uint256 i = 0; i < length; i++) { + ${name}[i] = ${decodeFunction} + } + result.${name} = ${name}` + } + } +} + +// TODO: abstract it out +let optionsAll = options => + options->Belt.Array.reduce(_, Some([]), (acc, obj) => { + switch (acc, obj) { + | (Some(acc'), Some(obj')) => Some(acc'->Js.Array.concat([obj'])) + | (_, _) => None + } + }) + +let generateDecodeLibSolidity = (schema, dataType) => { + let dataTypeString = dataType->dataTypeToString + let name = dataType->dataTypeToSchemaField + let template = (structs, functions) => + `library ${dataTypeString}Decoder { + using Obi for Obi.Data; + + struct ${dataTypeString} { + ${structs} + } + + function decode${dataTypeString}(bytes memory _data) + internal + pure + returns (${dataTypeString} memory result) + { + Obi.Data memory data = Obi.from(_data); + ${functions} + } +} + +` + + let fieldPairsOpt = extractFields(schema, name) + + fieldPairsOpt->Belt.Option.flatMap(fieldsPairs => { + let fieldsOpt = fieldsPairs->Belt.Array.map(parse)->optionsAll + fieldsOpt->Belt.Option.flatMap(fields => { + let indent = "\n " + Some( + template( + fields->Belt.Array.map(declareSolidity)->Js.Array.joinWith(indent, _), + fields->Belt.Array.map(assignSolidity)->Js.Array.joinWith(indent, _), + ), + ) + }) + }) +} + +let generateDecoderSolidity = schema => { + let template = `pragma solidity ^0.5.0; + +import "./Obi.sol"; + +` + let paramsCodeOpt = generateDecodeLibSolidity(schema, Params) + let resultCodeOpt = generateDecodeLibSolidity(schema, Result) + + paramsCodeOpt->Belt.Option.flatMap(paramsCode => { + resultCodeOpt->Belt.Option.flatMap(resultCode => Some(template ++ paramsCode ++ resultCode)) + }) +} + +// TODO: revisit when using this. +let declareGo = ({name, varType}) => { + let capitalizedName = name->ChangeCase.pascalCase + let type_ = switch varType { + | Single(String) => "string" + | Single(U64) => "uint64" + | Single(U32) => "uint32" + | Single(U8) => "uint8" + | Array(String) => "[]string" + | Array(U64) => "[]uint64" + | Array(U32) => "[]uint32" + | Array(U8) => "[]uint8" + } + j`$capitalizedName $type_` +} + +let assignGo = ({name, varType}) => { + switch varType { + | Single(String) => j`$name, err := decoder.DecodeString() + if err !== nil { + return Result{}, err + }` + | Single(U64) => j`$name, err := decoder.DecodeU64() + if err !== nil { + return Result{}, err + }` + | Single(U32) => j`$name, err := decoder.DecodeU32() + if err !== nil { + return Result{}, err + }` + | Single(U8) => j`$name, err := decoder.DecodeU8() + if err !== nil { + return Result{}, err + }` + | _ => "// TODO: implement later" + } +} + +let resultGo = ({name}) => { + let capitalizedName = name->ChangeCase.pascalCase + j`$capitalizedName: $name` +} + +// TODO: Implement input/params decoding +let generateDecoderGo = (packageName, schema, dataType) => { + switch dataType { + | Params => Some("Code is not available.") + | Result => + let name = dataType->dataTypeToSchemaField + let template = (structs, functions, results) => + j`package $packageName + +import "github.com/bandchain/chain/pkg/obi" + +type Result struct { +\t$structs +} + +func DecodeResult(data []byte) (Result, error) { +\tdecoder := obi.NewObiDecoder(data) + +\t$functions + +\tif !decoder.Finished() { +\t\treturn Result{}, errors.New("Obi: bytes left when decode result") +\t} + +\treturn Result{ +\t\t$results +\t}, nil +}` + + let fieldsPair = extractFields(schema, name)->Belt.Option.getExn + let fields = fieldsPair->Belt.Array.map(parse)->optionsAll->Belt.Option.getExn + Some( + template( + fields->Belt.Array.map(declareGo)->Js.Array.joinWith("\n\t", _), + fields->Belt.Array.map(assignGo)->Js.Array.joinWith("\n\t", _), + fields->Belt.Array.map(resultGo)->Js.Array.joinWith("\n\t\t", _), + ), + ) + } +} + +let encodeStructGo = ({name, varType}) => { + switch varType { + | Single(U8) => j`encoder.EncodeU8(result.$name)` + | Single(U32) => j`encoder.EncodeU32(result.$name)` + | Single(U64) => j`encoder.EncodeU64(result.$name)` + | Single(String) => j`encoder.EncodeString(result.$name)` + | _ => "//TODO: implement later" + } +} + +let generateEncodeGo = (packageName, schema, name) => { + let template = (structs, functions) => + j`package $packageName + +import "github.com/bandchain/chain/pkg/obi" + +type Result struct { +\t$structs +} + +func(result *Result) EncodeResult() []byte { +\tencoder := obi.NewObiEncoder() + +\t$functions + +\treturn encoder.GetEncodedData() +}` + + let fieldsPair = extractFields(schema, name)->Belt.Option.getExn + let fields = fieldsPair->Belt.Array.map(parse)->optionsAll->Belt.Option.getExn + Some( + template( + fields->Belt.Array.map(declareGo)->Js.Array.joinWith("\n\t", _), + fields->Belt.Array.map(encodeStructGo)->Js.Array.joinWith("\n\t", _), + ), + ) +} diff --git a/src/utils/proof/NonEVMProof.res b/src/utils/proof/NonEVMProof.res index a6c41e8f..6b5c4bfc 100644 --- a/src/utils/proof/NonEVMProof.res +++ b/src/utils/proof/NonEVMProof.res @@ -180,9 +180,9 @@ let decodeProof = { }) } let obi_encode_int = (i, n) => - Obi.encode( + Obi2.encode( "{x: " ++ n ++ "}/{_:u64}", - "input", + Obi2.Input, [{fieldName: "x", fieldValue: i->Belt.Int.toString}], )->Belt.Option.getExn @@ -200,9 +200,9 @@ type variant_of_proof_t = let rec encode = x => switch x { | RequestPacket({clientID, oracleScriptID, calldata, askCount, minCount}) => - Obi.encode( + Obi2.encode( "{clientID: string, oracleScriptID: u64, calldata: bytes, askCount: u64, minCount: u64}/{_:u64}", - "input", + Obi2.Input, [ {fieldName: "clientID", fieldValue: clientID}, {fieldName: "oracleScriptID", fieldValue: oracleScriptID->Belt.Int.toString}, @@ -221,9 +221,9 @@ let rec encode = x => resolveStatus, result, }) => - Obi.encode( + Obi2.encode( "{clientID: string, requestID: u64, ansCount: u64, requestTime: u64, resolveTime: u64, resolveStatus: u32, result: bytes}/{_:u64}", - "input", + Obi2.Input, [ {fieldName: "clientID", fieldValue: clientID}, {fieldName: "requestID", fieldValue: requestID->Belt.Int.toString}, @@ -236,9 +236,9 @@ let rec encode = x => ) | IAVLMerklePath({isDataOnRight, subtreeHeight, subtreeSize, subtreeVersion, siblingHash}) => - Obi.encode( + Obi2.encode( "{isDataOnRight: u8, subtreeHeight: u8, subtreeSize: u64, subtreeVersion: u64, siblingHash: bytes}/{_:u64}", - "input", + Obi2.Input, [ {fieldName: "isDataOnRight", fieldValue: isDataOnRight ? "1" : "0"}, {fieldName: "subtreeHeight", fieldValue: subtreeHeight->Belt.Int.toString}, @@ -287,9 +287,9 @@ let rec encode = x => lastResultsHash, evidenceAndProposerHash, }) => - Obi.encode( + Obi2.encode( "{versionAndChainIdHash: bytes, timeSecond: u64, timeNanoSecond: u32, lastBlockIDAndOther: bytes, nextValidatorHashAndConsensusHash: bytes, lastResultsHash: bytes, evidenceAndProposerHash: bytes}/{_:u64}", - "input", + Obi2.Input, [ { fieldName: "versionAndChainIdHash", @@ -317,9 +317,9 @@ let rec encode = x => ) | Signature({r, s, v, signedDataPrefix, signedDataSuffix}) => - Obi.encode( + Obi2.encode( "{r: bytes, s: bytes, v: u8, signedDataPrefix: bytes, signedDataSuffix: bytes}/{_:u64}", - "input", + Obi2.Input, [ {fieldName: "r", fieldValue: r->JsBuffer.toHex(~with0x=true)}, {fieldName: "s", fieldValue: s->JsBuffer.toHex(~with0x=true)}, @@ -360,9 +360,9 @@ let rec encode = x => let encodeReq = encode(RequestPacket(requestPacket))->Belt.Option.getExn let encodeRes = encode(ResponsePacket(responsePacket))->Belt.Option.getExn let encodeIAVLMerklePaths = encode(IAVLMerklePaths(iavlMerklePaths))->Belt.Option.getExn - Obi.encode( + Obi2.encode( "{blockHeight: u64, multiStore: bytes, blockMerkleParts: bytes, signatures: bytes, packet: bytes, version: u64, iavlPaths: bytes}/{_:u64}", - "input", + Obi2.Input, [ {fieldName: "blockHeight", fieldValue: blockHeight->Belt.Int.toString}, { From 01a4652f02521b2d585ebf3ffeefdea667121178 Mon Sep 17 00:00:00 2001 From: babybunn Date: Wed, 17 May 2023 17:27:03 +0700 Subject: [PATCH 2/5] test: create obi unit test --- __tests__/Obi_test.res | 326 +++++++++++++++++++++++++++++++++++++++++ src/utils/Obi2.res | 5 +- 2 files changed, 329 insertions(+), 2 deletions(-) create mode 100644 __tests__/Obi_test.res diff --git a/__tests__/Obi_test.res b/__tests__/Obi_test.res new file mode 100644 index 00000000..8e91dd74 --- /dev/null +++ b/__tests__/Obi_test.res @@ -0,0 +1,326 @@ +open Jest +open Obi2 +open Expect + +describe("Expect Obi to extract fields correctly", () => { + test("should be able to extract fields from bytes correctly", () => { + expect( + Some([ + {fieldName: "symbol", fieldType: "string"}, + {fieldName: "multiplier", fieldType: "u64"}, + ]), + )->toEqual(extractFields(`{symbol:string,multiplier:u64}/{volume:u64}`, "input")) + }) + + test("should return None on invalid type", () => { + expect(None)->toEqual(extractFields(`{symbol:string,multiplier:u64}/{volume:u64}`, "Input")) + }) +}) + +describe("Expect Obi encode correctly", () => { + test("should be able to encode input (string, int) correctly", () => { + expect(Some("00000003425443000000003b9aca00"->JsBuffer.fromHex))->toEqual( + encode( + `{symbol: string,multiplier: u64}/{price: u64,sources: [{ name: string, time: u64 }]}`, + Input, + [ + {fieldName: "symbol", fieldValue: "\"BTC\""}, + {fieldName: "multiplier", fieldValue: "1000000000"}, + ], + ), + ) + }) + + test("should be able to encode input (bytes) correctly", () => { + expect(Some("0000000a32323432353434333232"->JsBuffer.fromHex))->toEqual( + encode( + `{symbol: bytes}/{price: u64}`, + Input, + [ + { + fieldName: "symbol", + fieldValue: "\"2242544322\"", + }, + ], + ), + ) + }) + + test("should be able to encode nested input correctly", () => { + expect(Some("0x00000001000000020000000358585800000003595959"->JsBuffer.fromHex))->toEqual( + encode( + `{list: [{symbol: {name: [string]}}]}/{price: u64}`, + Input, + [{fieldName: "list", fieldValue: `[{"symbol": {"name": ["XXX", "YYY"]}}]`}], + ), + ) + }) + + test("should be able to encode output correctly", () => { + expect(Some("0x0000000200000000000000780000000000000143"->JsBuffer.fromHex))->toEqual( + encode( + `{list: [{symbol: {name: [string]}}]}/{price: [u64]}`, + Output, + [{fieldName: "price", fieldValue: `[120, 323]`}], + ), + ) + }) + + test("should return None if invalid data", () => { + expect(None)->toEqual( + encode( + `{symbol: string,multiplier: u64}/{price: u64,sources: [{ name: string, time: u64 }]}`, + Input, + [{fieldName: "symbol", fieldValue: "BTC"}], + ), + ) + }) + + test("should return None if invalid input schema", () => { + expect(None)->toEqual( + encode( + `{symbol: string}/{price: u64,sources: [{ name: string, time: u64 }]}`, + Input, + [ + {fieldName: "symbol", fieldValue: "BTC"}, + {fieldName: "multiplier", fieldValue: "1000000000"}, + ], + ), + ) + }) + + test("should return None if invalid output schema", () => { + expect(None)->toEqual( + encode(`{symbol: string}`, Output, [{fieldName: "symbol", fieldValue: "BTC"}]), + ) + }) +}) + +describe("Expect Obi decode correctly", () => { + test("should be able to decode from bytes correctly", () => { + expect( + Some([ + {fieldName: "symbol", fieldValue: "\"BTC\""}, + {fieldName: "multiplier", fieldValue: "1000000000"}, + ]), + )->toEqual( + decode( + `{symbol: string,multiplier: u64}/{price: u64,sources: [{ name: string, time: u64 }]}`, + Input, + "0x00000003425443000000003b9aca00"->JsBuffer.fromHex, + ), + ) + }) + + test("should be able to decode from bytes correctly (nested)", () => { + expect( + Some([{fieldName: "list", fieldValue: "[{\"symbol\":{\"name\":[\"XXX\",\"YYY\"]}}]"}]), + )->toEqual( + decode( + `{list: [{symbol: {name: [string]}}]}/{price: u64}`, + Input, + "0x00000001000000020000000358585800000003595959"->JsBuffer.fromHex, + ), + ) + }) + + test("should be able to decode from bytes correctly (bytes)", () => { + expect(Some([{fieldName: "symbol", fieldValue: "0x55555555"}]))->toEqual( + decode(`{symbol: bytes}/{price: u64}`, Input, "0x0000000455555555"->JsBuffer.fromHex), + ) + }) + + test("should return None if invalid schema", () => { + expect(None)->toEqual( + decode( + `{symbol: string}/{price: u64,sources: [{ name: string, time: u64 }]}`, + Input, + "0x00000003425443000000003b9aca00"->JsBuffer.fromHex, + ), + ) + }) +}) + +describe("should be able to generate solidity correctly", () => { + test("should be able to generate solidity", () => { + expect( + Some(`pragma solidity ^0.5.0; + +import "./Obi.sol"; + +library ParamsDecoder { + using Obi for Obi.Data; + + struct Params { + uint64 multiplier; + string symbol; + } + + function decodeParams(bytes memory _data) + internal + pure + returns (Params memory result) + { + Obi.Data memory data = Obi.from(_data); + result.multiplier = data.decodeU64(); + result.symbol = string(data.decodeBytes()); + } +} + +library ResultDecoder { + using Obi for Obi.Data; + + struct Result { + uint64 px; + } + + function decodeResult(bytes memory _data) + internal + pure + returns (Result memory result) + { + Obi.Data memory data = Obi.from(_data); + result.px = data.decodeU64(); + } +} + +`), + )->toEqual(generateDecoderSolidity(`{symbol:string,multiplier:u64}/{px:u64}`)) + }) + + test("should be able to generate solidity when parameter is array", () => { + expect( + Some(`pragma solidity ^0.5.0; + +import "./Obi.sol"; + +library ParamsDecoder { + using Obi for Obi.Data; + + struct Params { + uint64 multiplier; + string[] symbols; + } + + function decodeParams(bytes memory _data) + internal + pure + returns (Params memory result) + { + Obi.Data memory data = Obi.from(_data); + result.multiplier = data.decodeU64(); + uint32 length = data.decodeU32(); + string[] memory symbols = new string[](length); + for (uint256 i = 0; i < length; i++) { + symbols[i] = string(data.decodeBytes()); + } + result.symbols = symbols + } +} + +library ResultDecoder { + using Obi for Obi.Data; + + struct Result { + uint64[] rates; + } + + function decodeResult(bytes memory _data) + internal + pure + returns (Result memory result) + { + Obi.Data memory data = Obi.from(_data); + uint32 length = data.decodeU32(); + uint64[] memory rates = new uint64[](length); + for (uint256 i = 0; i < length; i++) { + rates[i] = data.decodeU64(); + } + result.rates = rates + } +} + +`), + )->toEqual(generateDecoderSolidity(`{symbols:[string],multiplier:u64}/{rates:[u64]}`)) + }) +}) + +describe("should be able to generate go code correctly", () => { + // TODO: Change to real generated code once golang ParamsDecode is implemented + test("should be able to generate go code 1", () => { + expect(Some(`Code is not available.`))->toEqual( + generateDecoderGo("main", `{symbol:string,multiplier:u64}/{px:u64}`, Obi2.Params), + ) + }) + test("should be able to generate go code 2", () => { + expect( + Some(`package test + +import "github.com/bandchain/chain/pkg/obi" + +type Result struct { + Px uint64 +} + +func DecodeResult(data []byte) (Result, error) { + decoder := obi.NewObiDecoder(data) + + px, err := decoder.DecodeU64() + if err !== nil { + return Result{}, err + } + + if !decoder.Finished() { + return Result{}, errors.New("Obi: bytes left when decode result") + } + + return Result{ + Px: px + }, nil +}`), + )->toEqual(generateDecoderGo("test", `{symbol:string,multiplier:u64}/{px:u64}`, Obi2.Result)) + }) +}) + +describe("should be able to generate encode go code correctly", () => { + test("should be able to generate encode go code 1", () => { + expect( + Some(`package main + +import "github.com/bandchain/chain/pkg/obi" + +type Result struct { + Multiplier uint64 + Symbol string +} + +func(result *Result) EncodeResult() []byte { + encoder := obi.NewObiEncoder() + + encoder.EncodeU64(result.multiplier) + encoder.EncodeString(result.symbol) + + return encoder.GetEncodedData() +}`), + )->toEqual(generateEncodeGo("main", `{symbol:string,multiplier:u64}/{px:u64}`, "input")) + }) + test("should be able to generate encode go code 2", () => { + expect( + Some(`package test + +import "github.com/bandchain/chain/pkg/obi" + +type Result struct { + Px uint64 +} + +func(result *Result) EncodeResult() []byte { + encoder := obi.NewObiEncoder() + + encoder.EncodeU64(result.px) + + return encoder.GetEncodedData() +}`), + )->toEqual(generateEncodeGo("test", `{symbol:string,multiplier:u64}/{px:u64}`, "output")) + }) +}) diff --git a/src/utils/Obi2.res b/src/utils/Obi2.res index 75166d4c..0e23f8f6 100644 --- a/src/utils/Obi2.res +++ b/src/utils/Obi2.res @@ -60,19 +60,20 @@ let extractFields: (string, string) => option> = %raw(` let encode = (schema, flow, valuePairs) => { open BandChainJS.Obi + switch { let typePairs = extractFields(schema, flow->flowToString)->Belt.Option.getExn + let dataPairs = typePairs->Belt.Array.map(({fieldName, fieldType}) => { let value = valuePairs ->Belt.Array.keepMap(each => fieldName == each.fieldName ? Some(each.fieldValue) : None) ->Belt.Array.getExn(0) - let parsed = value->Js.Json.parseExn (fieldName, parsed) }) - let data = Js.Json.object_(Js.Dict.fromArray(dataPairs)) + let data = Js.Json.object_(Js.Dict.fromArray(dataPairs)) let obi = create(schema) switch flow { | Input => obi->encodeInput(data) From 76e85b8d54d49a456c104bc087ba2124bada178e Mon Sep 17 00:00:00 2001 From: babybunn Date: Wed, 17 May 2023 18:04:08 +0700 Subject: [PATCH 3/5] rearrange test case --- __tests__/Obi_test.res | 80 ++++++++++++++++++++++-------------------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/__tests__/Obi_test.res b/__tests__/Obi_test.res index 8e91dd74..daf7e1b8 100644 --- a/__tests__/Obi_test.res +++ b/__tests__/Obi_test.res @@ -4,22 +4,22 @@ open Expect describe("Expect Obi to extract fields correctly", () => { test("should be able to extract fields from bytes correctly", () => { - expect( + expect(extractFields(`{symbol:string,multiplier:u64}/{volume:u64}`, "input"))->toEqual( Some([ {fieldName: "symbol", fieldType: "string"}, {fieldName: "multiplier", fieldType: "u64"}, ]), - )->toEqual(extractFields(`{symbol:string,multiplier:u64}/{volume:u64}`, "input")) + ) }) test("should return None on invalid type", () => { - expect(None)->toEqual(extractFields(`{symbol:string,multiplier:u64}/{volume:u64}`, "Input")) + expect(extractFields(`{symbol:string,multiplier:u64}/{volume:u64}`, "Input"))->toEqual(None) }) }) describe("Expect Obi encode correctly", () => { test("should be able to encode input (string, int) correctly", () => { - expect(Some("00000003425443000000003b9aca00"->JsBuffer.fromHex))->toEqual( + expect( encode( `{symbol: string,multiplier: u64}/{price: u64,sources: [{ name: string, time: u64 }]}`, Input, @@ -28,11 +28,11 @@ describe("Expect Obi encode correctly", () => { {fieldName: "multiplier", fieldValue: "1000000000"}, ], ), - ) + )->toEqual(Some("00000003425443000000003b9aca00"->JsBuffer.fromHex)) }) test("should be able to encode input (bytes) correctly", () => { - expect(Some("0000000a32323432353434333232"->JsBuffer.fromHex))->toEqual( + expect( encode( `{symbol: bytes}/{price: u64}`, Input, @@ -43,41 +43,41 @@ describe("Expect Obi encode correctly", () => { }, ], ), - ) + )->toEqual(Some("0000000a32323432353434333232"->JsBuffer.fromHex)) }) test("should be able to encode nested input correctly", () => { - expect(Some("0x00000001000000020000000358585800000003595959"->JsBuffer.fromHex))->toEqual( + expect( encode( `{list: [{symbol: {name: [string]}}]}/{price: u64}`, Input, [{fieldName: "list", fieldValue: `[{"symbol": {"name": ["XXX", "YYY"]}}]`}], ), - ) + )->toEqual(Some("0x00000001000000020000000358585800000003595959"->JsBuffer.fromHex)) }) test("should be able to encode output correctly", () => { - expect(Some("0x0000000200000000000000780000000000000143"->JsBuffer.fromHex))->toEqual( + expect( encode( `{list: [{symbol: {name: [string]}}]}/{price: [u64]}`, Output, [{fieldName: "price", fieldValue: `[120, 323]`}], ), - ) + )->toEqual(Some("0x0000000200000000000000780000000000000143"->JsBuffer.fromHex)) }) test("should return None if invalid data", () => { - expect(None)->toEqual( + expect( encode( `{symbol: string,multiplier: u64}/{price: u64,sources: [{ name: string, time: u64 }]}`, Input, [{fieldName: "symbol", fieldValue: "BTC"}], ), - ) + )->toEqual(None) }) test("should return None if invalid input schema", () => { - expect(None)->toEqual( + expect( encode( `{symbol: string}/{price: u64,sources: [{ name: string, time: u64 }]}`, Input, @@ -86,12 +86,12 @@ describe("Expect Obi encode correctly", () => { {fieldName: "multiplier", fieldValue: "1000000000"}, ], ), - ) + )->toEqual(None) }) test("should return None if invalid output schema", () => { - expect(None)->toEqual( - encode(`{symbol: string}`, Output, [{fieldName: "symbol", fieldValue: "BTC"}]), + expect(encode(`{symbol: string}`, Output, [{fieldName: "symbol", fieldValue: "BTC"}]))->toEqual( + None, ) }) }) @@ -99,51 +99,51 @@ describe("Expect Obi encode correctly", () => { describe("Expect Obi decode correctly", () => { test("should be able to decode from bytes correctly", () => { expect( - Some([ - {fieldName: "symbol", fieldValue: "\"BTC\""}, - {fieldName: "multiplier", fieldValue: "1000000000"}, - ]), - )->toEqual( decode( `{symbol: string,multiplier: u64}/{price: u64,sources: [{ name: string, time: u64 }]}`, Input, "0x00000003425443000000003b9aca00"->JsBuffer.fromHex, ), + )->toEqual( + Some([ + {fieldName: "symbol", fieldValue: "\"BTC\""}, + {fieldName: "multiplier", fieldValue: "1000000000"}, + ]), ) }) test("should be able to decode from bytes correctly (nested)", () => { expect( - Some([{fieldName: "list", fieldValue: "[{\"symbol\":{\"name\":[\"XXX\",\"YYY\"]}}]"}]), - )->toEqual( decode( `{list: [{symbol: {name: [string]}}]}/{price: u64}`, Input, "0x00000001000000020000000358585800000003595959"->JsBuffer.fromHex, ), + )->toEqual( + Some([{fieldName: "list", fieldValue: "[{\"symbol\":{\"name\":[\"XXX\",\"YYY\"]}}]"}]), ) }) test("should be able to decode from bytes correctly (bytes)", () => { - expect(Some([{fieldName: "symbol", fieldValue: "0x55555555"}]))->toEqual( + expect( decode(`{symbol: bytes}/{price: u64}`, Input, "0x0000000455555555"->JsBuffer.fromHex), - ) + )->toEqual(Some([{fieldName: "symbol", fieldValue: "0x55555555"}])) }) test("should return None if invalid schema", () => { - expect(None)->toEqual( + expect( decode( `{symbol: string}/{price: u64,sources: [{ name: string, time: u64 }]}`, Input, "0x00000003425443000000003b9aca00"->JsBuffer.fromHex, ), - ) + )->toEqual(None) }) }) describe("should be able to generate solidity correctly", () => { test("should be able to generate solidity", () => { - expect( + expect(generateDecoderSolidity(`{symbol:string,multiplier:u64}/{px:u64}`))->toEqual( Some(`pragma solidity ^0.5.0; import "./Obi.sol"; @@ -185,11 +185,11 @@ library ResultDecoder { } `), - )->toEqual(generateDecoderSolidity(`{symbol:string,multiplier:u64}/{px:u64}`)) + ) }) test("should be able to generate solidity when parameter is array", () => { - expect( + expect(generateDecoderSolidity(`{symbols:[string],multiplier:u64}/{rates:[u64]}`))->toEqual( Some(`pragma solidity ^0.5.0; import "./Obi.sol"; @@ -241,19 +241,21 @@ library ResultDecoder { } `), - )->toEqual(generateDecoderSolidity(`{symbols:[string],multiplier:u64}/{rates:[u64]}`)) + ) }) }) describe("should be able to generate go code correctly", () => { // TODO: Change to real generated code once golang ParamsDecode is implemented test("should be able to generate go code 1", () => { - expect(Some(`Code is not available.`))->toEqual( + expect( generateDecoderGo("main", `{symbol:string,multiplier:u64}/{px:u64}`, Obi2.Params), - ) + )->toEqual(Some(`Code is not available.`)) }) test("should be able to generate go code 2", () => { expect( + generateDecoderGo("test", `{symbol:string,multiplier:u64}/{px:u64}`, Obi2.Result), + )->toEqual( Some(`package test import "github.com/bandchain/chain/pkg/obi" @@ -278,13 +280,13 @@ func DecodeResult(data []byte) (Result, error) { Px: px }, nil }`), - )->toEqual(generateDecoderGo("test", `{symbol:string,multiplier:u64}/{px:u64}`, Obi2.Result)) + ) }) }) describe("should be able to generate encode go code correctly", () => { test("should be able to generate encode go code 1", () => { - expect( + expect(generateEncodeGo("main", `{symbol:string,multiplier:u64}/{px:u64}`, "input"))->toEqual( Some(`package main import "github.com/bandchain/chain/pkg/obi" @@ -302,10 +304,10 @@ func(result *Result) EncodeResult() []byte { return encoder.GetEncodedData() }`), - )->toEqual(generateEncodeGo("main", `{symbol:string,multiplier:u64}/{px:u64}`, "input")) + ) }) test("should be able to generate encode go code 2", () => { - expect( + expect(generateEncodeGo("test", `{symbol:string,multiplier:u64}/{px:u64}`, "output"))->toEqual( Some(`package test import "github.com/bandchain/chain/pkg/obi" @@ -321,6 +323,6 @@ func(result *Result) EncodeResult() []byte { return encoder.GetEncodedData() }`), - )->toEqual(generateEncodeGo("test", `{symbol:string,multiplier:u64}/{px:u64}`, "output")) + ) }) }) From 671337a885d3ec5496ab0539947d62b226c2a953 Mon Sep 17 00:00:00 2001 From: babybunn Date: Wed, 7 Jun 2023 13:17:52 +0700 Subject: [PATCH 4/5] fix: extractfields to typesafe --- __tests__/Obi_test.res | 14 ++-- .../oracle-script/OracleScriptBridgeCode.res | 2 +- .../oracle-script/OracleScriptExecute.res | 2 +- src/utils/Obi2.res | 66 ++++++++----------- 4 files changed, 33 insertions(+), 51 deletions(-) diff --git a/__tests__/Obi_test.res b/__tests__/Obi_test.res index daf7e1b8..06bf7215 100644 --- a/__tests__/Obi_test.res +++ b/__tests__/Obi_test.res @@ -4,17 +4,13 @@ open Expect describe("Expect Obi to extract fields correctly", () => { test("should be able to extract fields from bytes correctly", () => { - expect(extractFields(`{symbol:string,multiplier:u64}/{volume:u64}`, "input"))->toEqual( + expect(extractFields(`{symbol:string,multiplier:u64}/{volume:u64}`, Input))->toEqual( Some([ {fieldName: "symbol", fieldType: "string"}, {fieldName: "multiplier", fieldType: "u64"}, ]), ) }) - - test("should return None on invalid type", () => { - expect(extractFields(`{symbol:string,multiplier:u64}/{volume:u64}`, "Input"))->toEqual(None) - }) }) describe("Expect Obi encode correctly", () => { @@ -249,12 +245,12 @@ describe("should be able to generate go code correctly", () => { // TODO: Change to real generated code once golang ParamsDecode is implemented test("should be able to generate go code 1", () => { expect( - generateDecoderGo("main", `{symbol:string,multiplier:u64}/{px:u64}`, Obi2.Params), + generateDecoderGo("main", `{symbol:string,multiplier:u64}/{px:u64}`, Obi2.Input), )->toEqual(Some(`Code is not available.`)) }) test("should be able to generate go code 2", () => { expect( - generateDecoderGo("test", `{symbol:string,multiplier:u64}/{px:u64}`, Obi2.Result), + generateDecoderGo("test", `{symbol:string,multiplier:u64}/{px:u64}`, Obi2.Output), )->toEqual( Some(`package test @@ -286,7 +282,7 @@ func DecodeResult(data []byte) (Result, error) { describe("should be able to generate encode go code correctly", () => { test("should be able to generate encode go code 1", () => { - expect(generateEncodeGo("main", `{symbol:string,multiplier:u64}/{px:u64}`, "input"))->toEqual( + expect(generateEncodeGo("main", `{symbol:string,multiplier:u64}/{px:u64}`, Input))->toEqual( Some(`package main import "github.com/bandchain/chain/pkg/obi" @@ -307,7 +303,7 @@ func(result *Result) EncodeResult() []byte { ) }) test("should be able to generate encode go code 2", () => { - expect(generateEncodeGo("test", `{symbol:string,multiplier:u64}/{px:u64}`, "output"))->toEqual( + expect(generateEncodeGo("test", `{symbol:string,multiplier:u64}/{px:u64}`, Output))->toEqual( Some(`package test import "github.com/bandchain/chain/pkg/obi" diff --git a/src/components/oracle-script/OracleScriptBridgeCode.res b/src/components/oracle-script/OracleScriptBridgeCode.res index 20e7f071..2ee806dc 100644 --- a/src/components/oracle-script/OracleScriptBridgeCode.res +++ b/src/components/oracle-script/OracleScriptBridgeCode.res @@ -270,6 +270,6 @@ let make = (~schema) => {
{schema->renderCode}
- + } diff --git a/src/components/oracle-script/OracleScriptExecute.res b/src/components/oracle-script/OracleScriptExecute.res index c872dba4..33f401d6 100644 --- a/src/components/oracle-script/OracleScriptExecute.res +++ b/src/components/oracle-script/OracleScriptExecute.res @@ -473,7 +473,7 @@ module ExecutionPart = { @react.component let make = (~id: ID.OracleScript.t, ~schema: string) => { - let paramsInput = schema->Obi2.extractFields("input")->Belt.Option.getExn + let paramsInput = schema->Obi2.extractFields(Input)->Belt.Option.getExn Some() }->Belt.Option.getWithDefault( diff --git a/src/utils/Obi2.res b/src/utils/Obi2.res index 0e23f8f6..f1871871 100644 --- a/src/utils/Obi2.res +++ b/src/utils/Obi2.res @@ -18,51 +18,38 @@ let flowToString = x => | Output => "output" } -type data_type_t = - | Params - | Result - let dataTypeToString = dataType => switch dataType { - | Params => "Params" - | Result => "Result" + | Input => "Params" + | Output => "Result" } -let dataTypeToSchemaField = dataType => - switch dataType { - | Params => "input" - | Result => "output" - } +let extractFields = (schema, flow) => { + try { + let normalizedSchema = schema->Js.String2.replaceByRe(%re("/\s+/g"), "") -let extractFields: (string, string) => option> = %raw(` - function(schema, t) { - try { - const normalizedSchema = schema.replace(/\s+/g, '') - const tokens = normalizedSchema.split('/') - let val - if (t === 'input') { - val = tokens[0] - } else if (t === 'output') { - val = tokens[1] - } else { - return undefined - } - let specs = val.slice(1, val.length - 1).split(',') - return specs.map((spec) => { - let x = spec.split(':') - return {fieldName: x[0], fieldType: x[1]} - }) - } catch { - return undefined + let tokens = normalizedSchema->Js.String2.split("/") + let val = switch flow { + | Input => tokens[0] + | Output => tokens[1] } + let specs = val->Js.String2.slice(~from=1, ~to_=val->String.length - 1)->Js.String2.split(",") + Some( + specs->Belt.Array.map(spec => { + let x = spec->Js.String2.split(":") + {fieldName: x[0], fieldType: x[1]} + }), + ) + } catch { + | _ => None } -`) +} let encode = (schema, flow, valuePairs) => { open BandChainJS.Obi switch { - let typePairs = extractFields(schema, flow->flowToString)->Belt.Option.getExn + let typePairs = extractFields(schema, flow)->Belt.Option.getExn let dataPairs = typePairs->Belt.Array.map(({fieldName, fieldType}) => { let value = @@ -216,7 +203,7 @@ let optionsAll = options => let generateDecodeLibSolidity = (schema, dataType) => { let dataTypeString = dataType->dataTypeToString - let name = dataType->dataTypeToSchemaField + let name = dataType let template = (structs, functions) => `library ${dataTypeString}Decoder { using Obi for Obi.Data; @@ -259,8 +246,8 @@ let generateDecoderSolidity = schema => { import "./Obi.sol"; ` - let paramsCodeOpt = generateDecodeLibSolidity(schema, Params) - let resultCodeOpt = generateDecodeLibSolidity(schema, Result) + let paramsCodeOpt = generateDecodeLibSolidity(schema, Input) + let resultCodeOpt = generateDecodeLibSolidity(schema, Output) paramsCodeOpt->Belt.Option.flatMap(paramsCode => { resultCodeOpt->Belt.Option.flatMap(resultCode => Some(template ++ paramsCode ++ resultCode)) @@ -310,12 +297,11 @@ let resultGo = ({name}) => { j`$capitalizedName: $name` } -// TODO: Implement input/params decoding let generateDecoderGo = (packageName, schema, dataType) => { switch dataType { - | Params => Some("Code is not available.") - | Result => - let name = dataType->dataTypeToSchemaField + | Input => Some("Code is not available.") + | Output => + let name = dataType let template = (structs, functions, results) => j`package $packageName From c8547b65cbea6ae49d06ff46f230989179625baf Mon Sep 17 00:00:00 2001 From: xxibcill Date: Fri, 21 Jun 2024 15:53:37 +0700 Subject: [PATCH 5/5] fix RequestDetailsPage after merge --- src/pages/Example.res | 398 ------------------------------- src/pages/RequestDetailsPage.res | 97 +------- src/pages/Test.res | 6 - 3 files changed, 6 insertions(+), 495 deletions(-) delete mode 100644 src/pages/Example.res delete mode 100644 src/pages/Test.res diff --git a/src/pages/Example.res b/src/pages/Example.res deleted file mode 100644 index 93fab1e3..00000000 --- a/src/pages/Example.res +++ /dev/null @@ -1,398 +0,0 @@ -// module Styles = { -// open CssJs - -// let root = style(. [ -// backgroundColor(black), -// color(white), -// padding2(~v=px(10), ~h=px(20)), -// marginTop(Spacing.lg), -// Media.mobile([backgroundColor(pink)]), -// ]) - -// let padding = style(. [padding(px(20))]) -// } - -// @react.component -// let make = () => { -// let ({ThemeContext.isDarkMode: isDarkMode, theme}, toggle) = React.useContext( -// ThemeContext.context, -// ) -// let (_, dispatchModal) = React.useContext(ModalContext.context) - -// let currentTime = -// React.useContext(TimeContext.context) -> MomentRe.Moment.format(Config.timestampUseFormat) - -// Js.log(currentTime) - -// let send = () => { -// None->SubmitMsg.Send->SubmitTx->OpenModal->dispatchModal -// } - -// let pageSize = 5 -// let (value, setValue) = React.useState(_ => 0.) -// let (preValue, setPreValue) = React.useState(_ => 0.) -// let (page, setPage) = React.useState(_ => 1) -// let blockSub = BlockSub.getList(~page, ~pageSize, ()) -// let run = () => -// AxiosRequest.execute( -// AxiosRequest.t( -// ~executable="IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwppbXBvcnQgcmVxdWVzdHMKaW1wb3J0IHN5cwoKVVJMID0gImh0dHBzOi8vYXNpYS1zb3V0aGVhc3QyLXByaWNlLWNhY2hpbmcuY2xvdWRmdW5jdGlvbnMubmV0L3F1ZXJ5LXByaWNlIgpIRUFERVJTID0geyJDb250ZW50LVR5cGUiOiAiYXBwbGljYXRpb24vanNvbiJ9CgoKZGVmIG1haW4oc3ltYm9scyk6CiAgICBwYXlsb2FkID0geyJzb3VyY2UiOiAiY3J5cHRvX2NvbXBhcmUiLCAic3ltYm9scyI6IHN5bWJvbHN9CiAgICByID0gcmVxdWVzdHMucG9zdChVUkwsIGhlYWRlcnM9SEVBREVSUywganNvbj1wYXlsb2FkKQogICAgci5yYWlzZV9mb3Jfc3RhdHVzKCkKCiAgICBweHMgPSByLmpzb24oKQoKICAgIGlmIGxlbihweHMpICE9IGxlbihzeW1ib2xzKToKICAgICAgICByYWlzZSBFeGNlcHRpb24oIlNMVUdfQU5EX1NZTUJPTF9MRU5fTk9UX01BVENIIikKCiAgICByZXR1cm4gIiwiLmpvaW4ocHhzKQoKCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICB0cnk6CiAgICAgICAgcHJpbnQobWFpbihzeXMuYXJndlsxOl0pKQogICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBlOgogICAgICAgIHByaW50KHN0cihlKSwgZmlsZT1zeXMuc3RkZXJyKQogICAgICAgIHN5cy5leGl0KDEpCg==", -// ~calldata="BTC", -// ~timeout=5000, -// ), -// ) -// ->Promise.then(res => { -// Js.log(res) -// Promise.resolve() -// }) -// ->Promise.catch(err => { -// Js.log(err) -// Promise.resolve() -// }) -// ->ignore - -// // let pageSize = 5 -// // let (value, setValue) = React.useState(_ => 0.) -// // let (preValue, setPreValue) = React.useState(_ => 0.) -// // let blockSub = BlockSub.get(~height=10030000, ()) - -// // let markDown = "### Hello Word ``` code ``` **strong**" - -// // let update = () => setValue(prev => prev +. 20.) - -// // let capitalizedName = "hello world" -> ChangeCase.pascalCase - -// // let keyword = "testing" - -// // let client = BandChainJS.createClient("https://api-gm-lb.bandchain.org") -// // let _ = -// // client -// // ->BandChainJS.getReferenceData(["BAND/USD", "BAND/BTC"]) -// // ->Promise.then(result => { -// // Js.log(result) -// // Promise.resolve() -// // }) - -// // Js.log(capitalizedName) -// // Js.log(Theme.get(Theme.Day)) -// // let identity = "94C57647B928FAF1" -// // let useTest = AxiosHooks.use(j`https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=$identity&fields=pictures`) -// // Js.log2("use", useTest) - -// // let ( -// // data, -// // reload, -// // ) = AxiosHooks.useWithReload(j`https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=$identity&fields=pictures`) - -// // Js.log2("useWithReloadData", data) - -// // React.useEffect1(() => { -// // let handleKey = event => -// // if ReactEvent.Keyboard.keyCode(event) == 27 { -// // Js.log("trigger") -// // } -// // Document.addKeyboardEventListener("keydown", handleKey) -// // Some(() => Document.removeKeyboardEventListener("keydown", handleKey)) -// // }, []) -// // LocalStorage.setItem(keyword, "Hello World") -// // Js.log(Semver.gte("5.2.3", "3.2.1")) - -// // // address -// // let mnemonic = "absurd exhibit garbage gun flush gown basic chicken east image chimney stand skill own bracket" -// // let bandChain = CosmosJS.network("rpcUrl", "chainID") -// // bandChain->CosmosJS.setPath("m/44'/494'/0'/0/0") -// // bandChain->CosmosJS.setBech32MainPrefix("band") -// // Js.log2( -// // "private", -// // bandChain -> CosmosJS.getECPairPriv(_, mnemonic) -> JsBuffer.toHex(~with0x=false), -// // ) -// // Js.log2("address", bandChain -> CosmosJS.getAddress(_, mnemonic)) - -// // let countUp = CountUp.context( -// // CountUp.props( -// // ~start=preValue, -// // ~end=value, -// // ~delay=0, -// // ~decimals=6, -// // ~duration=4, -// // ~useEasing=false, -// // ~separator=",", -// // ~ref="counter", -// // ), -// // ) - -// Js.log(Env.rpc) - -// let chainID = "band-laozi-testnet2" - -// let (accountOpt, dispatchAccount) = React.useContext(AccountContext.context) - -// let connectMnemonic = () => { -// let wallet = Wallet.createFromMnemonic("s") -// let _ = -// wallet -// ->Wallet.getAddressAndPubKey -// ->Promise.then(((address, pubKey)) => { -// dispatchAccount(Connect(wallet, address, pubKey, chainID)) -// Js.log(accountOpt) -// Promise.resolve() -// }) -// ->Promise.catch(err => { -// Js.Console.log(err) -// Promise.resolve() -// }) -// } - -// let connectLedger = () => { -// let wallet = Wallet.createFromMnemonic("s") -// let _ = -// wallet -// ->Wallet.getAddressAndPubKey -// ->Promise.then(((address, pubKey)) => { -// dispatchAccount(Connect(wallet, address, pubKey, chainID)) -// Js.log(accountOpt) -// Promise.resolve() -// }) -// ->Promise.catch(err => { -// Js.Console.log(err) -// Promise.resolve() -// }) -// } - -// let connectLedger = () => { -// let _ = -// Wallet.createFromLedger(Ledger.Cosmos, 0) -// ->Promise.then(wallet => { -// wallet -// ->Wallet.getAddressAndPubKey -// ->Promise.then(((address, pubKey)) => { -// dispatchAccount(Connect(wallet, address, pubKey, chainID)) -// Promise.resolve() -// }) -// }) -// ->Promise.catch(err => { -// Js.Console.log(err) -// Promise.resolve() -// }) -// } - -// let disconnect = () => { -// dispatchAccount(Disconnect) -// } - -// let infoSub = React.useContext(GlobalContext.context) - -// <> -// React.string} -// placement="bottom" -// arrow=true -// leaveDelay=0 -// leaveTouchDelay=3000 -// enterTouchDelay=0> -// {React.string("Hello World")} -// -// -//
{ -// Copy.copy("Hello World") -// }}> -// {"Copy" -> React.string} -//
-//
-// {switch blockSub { -// | Data(blocks) => { -// Js.log2("Data is ", blocks) -// React.null -// } -// | _ => { -// Js.log("Loading...") -// "Loading...." -> React.string -// } -// }} -//
-// -// -// -// -// -// {switch accountOpt { -// | Some({address}) => address -> Address.toBech32 -> React.string -// | None => "not connected" -> React.string -// }} -// -// -// {"let x = hello world; console.log(x);" -> React.string} -// -// { -// let dict = Js.Dict.empty() -// Js.Dict.set(dict, "name", Js.Json.string("John Doe")) -// let src = Js.Json.object_(dict) - -// -// } -// {switch infoSub { -// | Data({financial}) => { -// Js.log(financial) -// React.null -// } -// | _ => React.null -// }} -// -// Address.fromBech32, -// 149, -// #account, -// ), -// ), -// ("XXXX", InfoMobileCard.Height(ID.Block.ID(1299))), -// ] -// idx={"aksdaksdkasd"} -// /> -// -// // let pageSize = 5 -// // let (value, setValue) = React.useState(_ => 0.) -// // let (preValue, setPreValue) = React.useState(_ => 0.) -// // let blockSub = BlockSub.get(~height=10030000, ()) - -// // let markDown = "### Hello Word ``` code ``` **strong**" - -// // let update = () => setValue(prev => prev +. 20.) - -// // let capitalizedName = "hello world" -> ChangeCase.pascalCase - -// // let keyword = "testing" - -// // let client = BandChainJS.createClient("https://api-gm-lb.bandchain.org") -// // let _ = -// // client -// // ->BandChainJS.getReferenceData(["BAND/USD", "BAND/BTC"]) -// // ->Promise.then(result => { -// // Js.log(result) -// // Promise.resolve() -// // }) - -// // Js.log(capitalizedName) -// // Js.log(Theme.get(Theme.Day)) -// // let identity = "94C57647B928FAF1" -// // let useTest = AxiosHooks.use(j`https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=$identity&fields=pictures`) -// // Js.log2("use", useTest) - -// // let ( -// // data, -// // reload, -// // ) = AxiosHooks.useWithReload(j`https://keybase.io/_/api/1.0/user/lookup.json?key_suffix=$identity&fields=pictures`) - -// // Js.log2("useWithReloadData", data) - -// // React.useEffect1(() => { -// // let handleKey = event => -// // if ReactEvent.Keyboard.keyCode(event) == 27 { -// // Js.log("trigger") -// // } -// // Document.addKeyboardEventListener("keydown", handleKey) -// // Some(() => Document.removeKeyboardEventListener("keydown", handleKey)) -// // }, []) -// // LocalStorage.setItem(keyword, "Hello World") -// // Js.log(Semver.gte("5.2.3", "3.2.1")) - -// // // address -// // let mnemonic = "absurd exhibit garbage gun flush gown basic chicken east image chimney stand skill own bracket" -// // let bandChain = CosmosJS.network("rpcUrl", "chainID") -// // bandChain->CosmosJS.setPath("m/44'/494'/0'/0/0") -// // bandChain->CosmosJS.setBech32MainPrefix("band") -// // Js.log2( -// // "private", -// // bandChain -> CosmosJS.getECPairPriv(_, mnemonic) -> JsBuffer.toHex(~with0x=false), -// // ) -// // Js.log2("address", bandChain -> CosmosJS.getAddress(_, mnemonic)) - -// // let countUp = CountUp.context( -// // CountUp.props( -// // ~start=preValue, -// // ~end=value, -// // ~delay=0, -// // ~decimals=6, -// // ~duration=4, -// // ~useEasing=false, -// // ~separator=",", -// // ~ref="counter", -// // ), -// // ) - -// // React.useEffect1(_ => { -// // countUp.update(value) -// // let timeoutId = Js.Global.setTimeout(() => setPreValue(_ => value), 800) -// // Some(() => Js.Global.clearTimeout(timeoutId)) -// // }, [value]) - -// // Js.log(LocalStorage.getItem(keyword)) - -// // <> -// // React.string} -// // placement="bottom" -// // arrow=true -// // leaveDelay=0 -// // leaveTouchDelay=3000 -// // enterTouchDelay=0> -// // {React.string("Hello World")} -// // -// // -// // -// //
{ -// // Copy.copy("Hello World") -// // }}> -// // {"Copy" -> React.string} -// //
-// // -// // -// // -// // -// // {"let x = hello world; console.log(x);" -> React.string} -// // -// //
-// // {switch blockSub { -// // | {data: Some({blocks_by_pk}), loading: false} => { -// // Js.log2("Data is ", blocks_by_pk) -// // React.null -// // } -// // | {loading: true, data: Some(x)} => { -// // Js.log2("Loading with Some", x) -// //
{"Loading with Some" -> React.string}
-// // } -// // | {loading: true, data: None} => { -// // Js.log("Loading with None") -// //
{"Loading with None" -> React.string}
-// // } -// // | {error: Some(error)} => { -// // Js.log(error) -// // React.null -// // } -// // | {loading: false, data: None, error: None} => { -// // Js.log("No data") -// //
{"No data" -> React.string}
-// // } -// // }} -// //
-// // { -// // let dict = Js.Dict.empty() -// // Js.Dict.set(dict, "name", Js.Json.string("John Doe")) -// // let src = Js.Json.object_(dict) - -// // -// // } -// // -// } - diff --git a/src/pages/RequestDetailsPage.res b/src/pages/RequestDetailsPage.res index db02a808..38e319fd 100644 --- a/src/pages/RequestDetailsPage.res +++ b/src/pages/RequestDetailsPage.res @@ -314,18 +314,6 @@ let make = (~reqID) => { {switch requestSub { | Data({transactionOpt}) => - // switch transactionOpt { - // | Some({gasFee}) => - // Coin.getBandAmountFromCoins - // ->Format.fPretty(~digits=6) ++ " BAND"} - // size=Text.Body1 - // color={theme.neutral_600} - // /> - // | None => - // } { }} - - - - - // - // {switch requestSub { - // | Data({transactionOpt}) => - // switch transactionOpt { - // | Some({gasFee}) => - // Coin.getBandAmountFromCoins - // ->Format.fPretty(~digits=6) ++ " BAND"} - // size=Text.Body1 - // color={theme.neutral_600} - // /> - // | None => - // } - // | _ => - // }} - // - - - - - - - {switch requestSub { - | Data({prepareGas}) => - Belt.Int.toString} - size=Text.Body1 - color={theme.neutral_600} - /> - | _ => - }} - - - - - - - - {switch requestSub { - | Data({executeGas}) => - Belt.Int.toString} - size=Text.Body1 - color={theme.neutral_600} - /> - | _ => - }} - - { color={theme.neutral_600} /> - // - // {switch requestSub { - // | Data({transactionOpt}) => - // switch transactionOpt { - // | Some({blockHeight}) => - // | None => - // } - // | _ => - // }} - // + + {switch requestSub { + | Data({transactionOpt}) => + | _ => + }} + diff --git a/src/pages/Test.res b/src/pages/Test.res deleted file mode 100644 index b4b95aee..00000000 --- a/src/pages/Test.res +++ /dev/null @@ -1,6 +0,0 @@ -@react.component -let make = () => { -
-

{"Test"->React.string}

-
-}