From d79fba726472a6d4689fcdc2e429e20cd6e0c8ee Mon Sep 17 00:00:00 2001 From: Burnt Val Date: Thu, 10 Oct 2024 15:22:35 -0400 Subject: [PATCH 1/5] default to auto fee on signAndBroadcast --- packages/abstraxion-core/src/GranteeSignerClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/abstraxion-core/src/GranteeSignerClient.ts b/packages/abstraxion-core/src/GranteeSignerClient.ts index ce726d21..32b4f599 100644 --- a/packages/abstraxion-core/src/GranteeSignerClient.ts +++ b/packages/abstraxion-core/src/GranteeSignerClient.ts @@ -93,7 +93,7 @@ export class GranteeSignerClient extends SigningCosmWasmClient { public async signAndBroadcast( signerAddress: string, messages: readonly EncodeObject[], - fee: StdFee | "auto" | number, + fee: StdFee | "auto" | number = "auto", memo = "", ): Promise { // Figure out if the signerAddress is a granter From 67e515252ff8bfe7c18594de5b321da00936a8a3 Mon Sep 17 00:00:00 2001 From: Burnt Val Date: Fri, 11 Oct 2024 12:28:50 -0400 Subject: [PATCH 2/5] overwrite signAndBroadcast method to handle simulation and default to auto fee --- apps/demo-app/src/app/layout.tsx | 2 +- apps/demo-app/src/app/page.tsx | 42 +++++++++------ .../src/GranteeSignerClient.ts | 51 +++++++++++++++++-- 3 files changed, 76 insertions(+), 19 deletions(-) diff --git a/apps/demo-app/src/app/layout.tsx b/apps/demo-app/src/app/layout.tsx index ff657102..f5ace73a 100644 --- a/apps/demo-app/src/app/layout.tsx +++ b/apps/demo-app/src/app/layout.tsx @@ -32,7 +32,7 @@ const legacyConfig = { }; const treasuryConfig = { - treasury: "xion17ah4x9te3sttpy2vj5x6hv4xvc0td526nu0msf7mt3kydqj4qs2s9jhe90", // Example XION treasury contract + treasury: "xion1h82c0efsxxq4pgua754u6xepfu6avglup20fl834gc2ah0ptgn5s2zffe9", // Example XION treasury contract with /cosmwasm.wasm.v1.MsgExecuteContract grant // Optional params to activate mainnet config // rpcUrl: "https://rpc.xion-mainnet-1.burnt.com:443", // restUrl: "https://api.xion-mainnet-1.burnt.com:443", diff --git a/apps/demo-app/src/app/page.tsx b/apps/demo-app/src/app/page.tsx index c2eafddd..d9d84045 100644 --- a/apps/demo-app/src/app/page.tsx +++ b/apps/demo-app/src/app/page.tsx @@ -9,14 +9,12 @@ import { } from "@burnt-labs/abstraxion"; import { Button } from "@burnt-labs/ui"; import "@burnt-labs/ui/dist/index.css"; -import type { ExecuteResult } from "@cosmjs/cosmwasm-stargate"; +import type { DeliverTxResponse } from "@cosmjs/cosmwasm-stargate"; import { SignArb } from "../components/sign-arb.tsx"; const seatContractAddress = "xion1z70cvc08qv5764zeg3dykcyymj5z6nu4sqr7x8vl4zjef2gyp69s9mmdka"; -type ExecuteResultOrUndefined = ExecuteResult | undefined; - export default function Page(): JSX.Element { // Abstraxion hooks const { data: account } = useAbstraxionAccount(); @@ -28,8 +26,9 @@ export default function Page(): JSX.Element { React.Dispatch>, ] = useModal(); const [loading, setLoading] = useState(false); - const [executeResult, setExecuteResult] = - useState(undefined); + const [executeResult, setExecuteResult] = useState< + DeliverTxResponse | undefined + >(undefined); const blockExplorerUrl = `https://explorer.burnt.com/xion-testnet-1/tx/${executeResult?.transactionHash}`; @@ -58,17 +57,30 @@ export default function Page(): JSX.Element { }; try { - const claimRes = await client?.execute( - account.bech32Address, - seatContractAddress, - msg, - { - amount: [{ amount: "0", denom: "uxion" }], - gas: "500000", + const msgSerialized = new TextEncoder().encode(JSON.stringify(msg)); + const msgExecuteContract = { + typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", + value: { + sender: account.bech32Address, + contract: seatContractAddress, + msg: msgSerialized, + funds: [], }, - "", - [], - ); + }; + + const claimRes = await client?.signAndBroadcast(account.bech32Address, [ + msgExecuteContract, + ]); + + // OR + // const claimRes = await client?.execute( + // account.bech32Address, + // seatContractAddress, + // msg, + // "auto", + // "", + // [], + // ); setExecuteResult(claimRes); } catch (error) { diff --git a/packages/abstraxion-core/src/GranteeSignerClient.ts b/packages/abstraxion-core/src/GranteeSignerClient.ts index 32b4f599..2491edf1 100644 --- a/packages/abstraxion-core/src/GranteeSignerClient.ts +++ b/packages/abstraxion-core/src/GranteeSignerClient.ts @@ -8,8 +8,14 @@ import { EncodeObject, OfflineSigner, } from "@cosmjs/proto-signing"; -import type { Account, SignerData, StdFee } from "@cosmjs/stargate"; -import type { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; +import { + calculateFee, + GasPrice, + type Account, + type SignerData, + type StdFee, +} from "@cosmjs/stargate"; +import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; import { MsgExec } from "cosmjs-types/cosmos/authz/v1beta1/tx"; import { HttpEndpoint, @@ -17,6 +23,7 @@ import { TendermintClient, } from "@cosmjs/tendermint-rpc"; import { customAccountFromAny } from "@burnt-labs/signers"; +import { xionGasValues } from "@burnt-labs/constants"; interface GranteeSignerOptions { readonly granterAddress: string; @@ -111,7 +118,45 @@ export class GranteeSignerClient extends SigningCosmWasmClient { ]; } - return super.signAndBroadcast(signerAddress, messages, fee, memo); + let usedFee: StdFee; + const { + gasPrice: gasPriceString, + gasAdjustment, + gasAdjustmentMargin, + } = xionGasValues; + if (fee == "auto" || typeof fee === "number") { + const gasEstimation = await this.simulate(signerAddress, messages, memo); + const multiplier = typeof fee == "number" ? fee : gasAdjustment; + const gasPrice = GasPrice.fromString(gasPriceString); + // usedFee = calculateFee(Math.round(gasEstimation * multiplier), gasPrice); + const calculatedFee = calculateFee(gasEstimation, gasPrice); + let gas = Math.ceil( + parseInt(calculatedFee.gas) * multiplier + gasAdjustmentMargin, + ).toString(); + + const chainId = await this.getChainId(); + if (/testnet/.test(chainId)) { + usedFee = { amount: [{ amount: "0", denom: "uxion" }], gas }; + } else { + usedFee = { amount: calculatedFee.amount, gas }; + } + } else { + usedFee = fee; + } + + const txRaw = await this.sign( + signerAddress, + messages, + usedFee, + memo, + undefined, + ); + const txBytes = TxRaw.encode(txRaw).finish(); + return this.broadcastTx( + txBytes, + this.broadcastTimeoutMs, + this.broadcastPollIntervalMs, + ); } public async sign( From 7db321cbc8c9b4e1fb6ef20017d5e629d715128b Mon Sep 17 00:00:00 2001 From: Burnt Val Date: Fri, 11 Oct 2024 17:25:22 -0400 Subject: [PATCH 3/5] restore signAndBroadcast; introduce gasPrice param in provider; comments on setting fee in demo app --- apps/demo-app/src/app/layout.tsx | 1 + apps/demo-app/src/app/page.tsx | 39 ++++++-------- .../src/GranteeSignerClient.ts | 53 ++----------------- .../src/components/Abstraxion/index.tsx | 2 + .../components/AbstraxionContext/index.tsx | 4 ++ .../src/hooks/useAbstraxionSigningClient.ts | 24 +++++++-- 6 files changed, 48 insertions(+), 75 deletions(-) diff --git a/apps/demo-app/src/app/layout.tsx b/apps/demo-app/src/app/layout.tsx index f5ace73a..3cae284c 100644 --- a/apps/demo-app/src/app/layout.tsx +++ b/apps/demo-app/src/app/layout.tsx @@ -33,6 +33,7 @@ const legacyConfig = { const treasuryConfig = { treasury: "xion1h82c0efsxxq4pgua754u6xepfu6avglup20fl834gc2ah0ptgn5s2zffe9", // Example XION treasury contract with /cosmwasm.wasm.v1.MsgExecuteContract grant + // gasPrice: "0uxion", // This defaults to "0uxion" on testnet and "0.025uxion" on mainnet. If you feel the need to change the gasPrice when connecting to signer, set this value. Please stick to the string format seen in example // Optional params to activate mainnet config // rpcUrl: "https://rpc.xion-mainnet-1.burnt.com:443", // restUrl: "https://api.xion-mainnet-1.burnt.com:443", diff --git a/apps/demo-app/src/app/page.tsx b/apps/demo-app/src/app/page.tsx index d9d84045..a41a02d2 100644 --- a/apps/demo-app/src/app/page.tsx +++ b/apps/demo-app/src/app/page.tsx @@ -9,12 +9,14 @@ import { } from "@burnt-labs/abstraxion"; import { Button } from "@burnt-labs/ui"; import "@burnt-labs/ui/dist/index.css"; -import type { DeliverTxResponse } from "@cosmjs/cosmwasm-stargate"; +import type { ExecuteResult } from "@cosmjs/cosmwasm-stargate"; import { SignArb } from "../components/sign-arb.tsx"; const seatContractAddress = "xion1z70cvc08qv5764zeg3dykcyymj5z6nu4sqr7x8vl4zjef2gyp69s9mmdka"; +type ExecuteResultOrUndefined = ExecuteResult | undefined; + export default function Page(): JSX.Element { // Abstraxion hooks const { data: account } = useAbstraxionAccount(); @@ -26,9 +28,8 @@ export default function Page(): JSX.Element { React.Dispatch>, ] = useModal(); const [loading, setLoading] = useState(false); - const [executeResult, setExecuteResult] = useState< - DeliverTxResponse | undefined - >(undefined); + const [executeResult, setExecuteResult] = + useState(undefined); const blockExplorerUrl = `https://explorer.burnt.com/xion-testnet-1/tx/${executeResult?.transactionHash}`; @@ -57,29 +58,23 @@ export default function Page(): JSX.Element { }; try { - const msgSerialized = new TextEncoder().encode(JSON.stringify(msg)); - const msgExecuteContract = { - typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract", - value: { - sender: account.bech32Address, - contract: seatContractAddress, - msg: msgSerialized, - funds: [], - }, - }; - - const claimRes = await client?.signAndBroadcast(account.bech32Address, [ - msgExecuteContract, - ]); + // Use "auto" fee for most transactions + const claimRes = await client?.execute( + account.bech32Address, + seatContractAddress, + msg, + "auto", + ); - // OR + // Default cosmsjs gas multiplier for simulation is 1.4 + // If you're finding that transactions are undersimulating, you can bump up the gas multiplier by setting fee to a number, ex. 1.5 + // Fee amounts shouldn't stray too far away from the defaults + // Example: // const claimRes = await client?.execute( // account.bech32Address, // seatContractAddress, // msg, - // "auto", - // "", - // [], + // 1.5, // ); setExecuteResult(claimRes); diff --git a/packages/abstraxion-core/src/GranteeSignerClient.ts b/packages/abstraxion-core/src/GranteeSignerClient.ts index 2491edf1..ce726d21 100644 --- a/packages/abstraxion-core/src/GranteeSignerClient.ts +++ b/packages/abstraxion-core/src/GranteeSignerClient.ts @@ -8,14 +8,8 @@ import { EncodeObject, OfflineSigner, } from "@cosmjs/proto-signing"; -import { - calculateFee, - GasPrice, - type Account, - type SignerData, - type StdFee, -} from "@cosmjs/stargate"; -import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; +import type { Account, SignerData, StdFee } from "@cosmjs/stargate"; +import type { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx"; import { MsgExec } from "cosmjs-types/cosmos/authz/v1beta1/tx"; import { HttpEndpoint, @@ -23,7 +17,6 @@ import { TendermintClient, } from "@cosmjs/tendermint-rpc"; import { customAccountFromAny } from "@burnt-labs/signers"; -import { xionGasValues } from "@burnt-labs/constants"; interface GranteeSignerOptions { readonly granterAddress: string; @@ -100,7 +93,7 @@ export class GranteeSignerClient extends SigningCosmWasmClient { public async signAndBroadcast( signerAddress: string, messages: readonly EncodeObject[], - fee: StdFee | "auto" | number = "auto", + fee: StdFee | "auto" | number, memo = "", ): Promise { // Figure out if the signerAddress is a granter @@ -118,45 +111,7 @@ export class GranteeSignerClient extends SigningCosmWasmClient { ]; } - let usedFee: StdFee; - const { - gasPrice: gasPriceString, - gasAdjustment, - gasAdjustmentMargin, - } = xionGasValues; - if (fee == "auto" || typeof fee === "number") { - const gasEstimation = await this.simulate(signerAddress, messages, memo); - const multiplier = typeof fee == "number" ? fee : gasAdjustment; - const gasPrice = GasPrice.fromString(gasPriceString); - // usedFee = calculateFee(Math.round(gasEstimation * multiplier), gasPrice); - const calculatedFee = calculateFee(gasEstimation, gasPrice); - let gas = Math.ceil( - parseInt(calculatedFee.gas) * multiplier + gasAdjustmentMargin, - ).toString(); - - const chainId = await this.getChainId(); - if (/testnet/.test(chainId)) { - usedFee = { amount: [{ amount: "0", denom: "uxion" }], gas }; - } else { - usedFee = { amount: calculatedFee.amount, gas }; - } - } else { - usedFee = fee; - } - - const txRaw = await this.sign( - signerAddress, - messages, - usedFee, - memo, - undefined, - ); - const txBytes = TxRaw.encode(txRaw).finish(); - return this.broadcastTx( - txBytes, - this.broadcastTimeoutMs, - this.broadcastPollIntervalMs, - ); + return super.signAndBroadcast(signerAddress, messages, fee, memo); } public async sign( diff --git a/packages/abstraxion/src/components/Abstraxion/index.tsx b/packages/abstraxion/src/components/Abstraxion/index.tsx index ec4aa154..96b24f66 100644 --- a/packages/abstraxion/src/components/Abstraxion/index.tsx +++ b/packages/abstraxion/src/components/Abstraxion/index.tsx @@ -69,6 +69,7 @@ export interface AbstraxionConfig { bank?: SpendLimit[]; callbackUrl?: string; treasury?: string; + gasPrice?: string; } export function AbstraxionProvider({ @@ -87,6 +88,7 @@ export function AbstraxionProvider({ bank={config.bank} callbackUrl={config.callbackUrl} treasury={config.treasury} + gasPrice={config.gasPrice} > {children} diff --git a/packages/abstraxion/src/components/AbstraxionContext/index.tsx b/packages/abstraxion/src/components/AbstraxionContext/index.tsx index 1d54150a..fefbf8c1 100644 --- a/packages/abstraxion/src/components/AbstraxionContext/index.tsx +++ b/packages/abstraxion/src/components/AbstraxionContext/index.tsx @@ -34,6 +34,7 @@ export interface AbstraxionContextProps { stake?: boolean; bank?: SpendLimit[]; treasury?: string; + gasPrice?: string; logout: () => void; } @@ -50,6 +51,7 @@ export function AbstraxionContextProvider({ bank, callbackUrl, treasury, + gasPrice, }: { children: ReactNode; contracts?: ContractGrantDescription[]; @@ -60,6 +62,7 @@ export function AbstraxionContextProvider({ bank?: SpendLimit[]; callbackUrl?: string; treasury?: string; + gasPrice?: string; }): JSX.Element { const [abstraxionError, setAbstraxionError] = useState(""); const [isConnected, setIsConnected] = useState(false); @@ -160,6 +163,7 @@ export function AbstraxionContextProvider({ bank, treasury, logout, + gasPrice, }} > {children} diff --git a/packages/abstraxion/src/hooks/useAbstraxionSigningClient.ts b/packages/abstraxion/src/hooks/useAbstraxionSigningClient.ts index 1a9668a5..f07b333a 100644 --- a/packages/abstraxion/src/hooks/useAbstraxionSigningClient.ts +++ b/packages/abstraxion/src/hooks/useAbstraxionSigningClient.ts @@ -1,5 +1,5 @@ import { useContext, useEffect, useState } from "react"; -import { testnetChainInfo } from "@burnt-labs/constants"; +import { testnetChainInfo, xionGasValues } from "@burnt-labs/constants"; import { GasPrice } from "@cosmjs/stargate"; import { GranteeSignerClient, @@ -15,8 +15,14 @@ export const useAbstraxionSigningClient = (): { | undefined; readonly logout: (() => void) | undefined; } => { - const { isConnected, abstraxionAccount, granterAddress, rpcUrl, logout } = - useContext(AbstraxionContext); + const { + isConnected, + abstraxionAccount, + granterAddress, + rpcUrl, + logout, + gasPrice, + } = useContext(AbstraxionContext); const [signArbWallet, setSignArbWallet] = useState< SignArbSecp256k1HdWallet | undefined >(undefined); @@ -45,12 +51,22 @@ export const useAbstraxionSigningClient = (): { return accounts[0].address; }); + let gasPriceDefault: GasPrice; + const { gasPrice: gasPriceConstant } = xionGasValues; + if (rpcUrl.includes("mainnet")) { + gasPriceDefault = GasPrice.fromString(gasPriceConstant); + } else { + gasPriceDefault = GasPrice.fromString("0uxion"); + } + const directClient = await GranteeSignerClient.connectWithSigner( // Should be set in the context but defaulting here just in case rpcUrl || testnetChainInfo.rpc, abstraxionAccount, { - gasPrice: GasPrice.fromString("0uxion"), + gasPrice: gasPrice + ? GasPrice.fromString(gasPrice) + : gasPriceDefault, granterAddress, granteeAddress, }, From 0b09db01a7c044fb366a162b621ea87cacbd95d2 Mon Sep 17 00:00:00 2001 From: Burnt Val Date: Thu, 17 Oct 2024 13:14:50 -0400 Subject: [PATCH 4/5] move gasPrice default logic to context --- .../src/components/AbstraxionContext/index.tsx | 14 +++++++++++--- .../src/hooks/useAbstraxionSigningClient.ts | 12 +----------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/abstraxion/src/components/AbstraxionContext/index.tsx b/packages/abstraxion/src/components/AbstraxionContext/index.tsx index fefbf8c1..46793351 100644 --- a/packages/abstraxion/src/components/AbstraxionContext/index.tsx +++ b/packages/abstraxion/src/components/AbstraxionContext/index.tsx @@ -1,6 +1,7 @@ import type { ReactNode } from "react"; import { createContext, useCallback, useEffect, useState } from "react"; -import { testnetChainInfo } from "@burnt-labs/constants"; +import { testnetChainInfo, xionGasValues } from "@burnt-labs/constants"; +import { GasPrice } from "@cosmjs/stargate"; import { SignArbSecp256k1HdWallet } from "@burnt-labs/abstraxion-core"; import { abstraxionAuth } from "../Abstraxion"; @@ -34,7 +35,7 @@ export interface AbstraxionContextProps { stake?: boolean; bank?: SpendLimit[]; treasury?: string; - gasPrice?: string; + gasPrice: GasPrice; logout: () => void; } @@ -73,6 +74,13 @@ export function AbstraxionContextProvider({ >(undefined); const [granterAddress, setGranterAddress] = useState(""); const [dashboardUrl, setDashboardUrl] = useState(""); + let gasPriceDefault: GasPrice; + const { gasPrice: gasPriceConstant } = xionGasValues; + if (rpcUrl.includes("mainnet")) { + gasPriceDefault = GasPrice.fromString(gasPriceConstant); + } else { + gasPriceDefault = GasPrice.fromString("0uxion"); + } const configureInstance = useCallback(() => { abstraxionAuth.configureAbstraxionInstance( @@ -163,7 +171,7 @@ export function AbstraxionContextProvider({ bank, treasury, logout, - gasPrice, + gasPrice: gasPrice ? GasPrice.fromString(gasPrice) : gasPriceDefault, }} > {children} diff --git a/packages/abstraxion/src/hooks/useAbstraxionSigningClient.ts b/packages/abstraxion/src/hooks/useAbstraxionSigningClient.ts index f07b333a..acddd2fc 100644 --- a/packages/abstraxion/src/hooks/useAbstraxionSigningClient.ts +++ b/packages/abstraxion/src/hooks/useAbstraxionSigningClient.ts @@ -51,22 +51,12 @@ export const useAbstraxionSigningClient = (): { return accounts[0].address; }); - let gasPriceDefault: GasPrice; - const { gasPrice: gasPriceConstant } = xionGasValues; - if (rpcUrl.includes("mainnet")) { - gasPriceDefault = GasPrice.fromString(gasPriceConstant); - } else { - gasPriceDefault = GasPrice.fromString("0uxion"); - } - const directClient = await GranteeSignerClient.connectWithSigner( // Should be set in the context but defaulting here just in case rpcUrl || testnetChainInfo.rpc, abstraxionAccount, { - gasPrice: gasPrice - ? GasPrice.fromString(gasPrice) - : gasPriceDefault, + gasPrice, granterAddress, granteeAddress, }, From a3b89b6abd16fe5b9a0553e0d1c93add058493f7 Mon Sep 17 00:00:00 2001 From: Burnt Val Date: Thu, 17 Oct 2024 13:16:32 -0400 Subject: [PATCH 5/5] changeset --- .changeset/perfect-hounds-hear.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/perfect-hounds-hear.md diff --git a/.changeset/perfect-hounds-hear.md b/.changeset/perfect-hounds-hear.md new file mode 100644 index 00000000..4da6b47d --- /dev/null +++ b/.changeset/perfect-hounds-hear.md @@ -0,0 +1,6 @@ +--- +"@burnt-labs/abstraxion": minor +"demo-app": minor +--- + +introduce gasPrice param; provide documentation on "auto" fee use