From 3d73a0d16212306edb0350e4d732195be519e228 Mon Sep 17 00:00:00 2001 From: Randy Date: Thu, 27 Jun 2024 11:23:28 +0800 Subject: [PATCH 1/4] add evmInfo to keplr chain info for testnet, localhost and devnet environments --- package.json | 2 +- src/constant/ibc.ts | 8 +------- src/provider/keplr/KeplrAccount.ts | 8 ++++++-- yarn.lock | 31 ++++-------------------------- 4 files changed, 12 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index a773dd69..d6073623 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@cosmos-kit/leap": "^0.13.14", "@improbable-eng/grpc-web": "^0.15.0", "@improbable-eng/grpc-web-node-http-transport": "^0.15.0", - "@keplr-wallet/types": "^0.11.64", + "@keplr-wallet/types": "^0.12.107", "@ledgerhq/hw-transport-webhid": "^6.20.0", "@ledgerhq/hw-transport-webusb": "^6.20.0", "@metamask/detect-provider": "^2.0.0", diff --git a/src/constant/ibc.ts b/src/constant/ibc.ts index 037c6dd0..d0f65817 100644 --- a/src/constant/ibc.ts +++ b/src/constant/ibc.ts @@ -202,7 +202,6 @@ export const EmbedChainInfosInit: SimpleMap = { coinGeckoId: "cosmos", }, ], - coinType: 118, features: ["ibc-transfer", "ibc-go"], explorerUrlToTx: "https://www.mintscan.io/cosmos/txs/{txHash}", tmRpc: "https://rpc.cosmos.network/", @@ -301,7 +300,6 @@ export const EmbedChainInfosInit: SimpleMap = { coinGeckoId: "secret", }, ], - coinType: 118, features: ["ibc-transfer", "ibc-go"], explorerUrlToTx: "https://secretnodes.com/secret/chains/secret-4/transactions/{txHash}", }, @@ -336,7 +334,6 @@ export const EmbedChainInfosInit: SimpleMap = { coinGeckoId: "akash-network", }, ], - coinType: 118, features: ["ibc-transfer", "ibc-go"], explorerUrlToTx: "https://www.mintscan.io/akash/txs/{txHash}", }, @@ -346,7 +343,7 @@ export const EmbedChainInfosInit: SimpleMap = { chainId: "regen-1", chainName: "Regen Network", stakeCurrency: { - coinDenom: "REGEN", + coinDenom: "REGEN", coinMinimalDenom: "uregen", coinDecimals: 6, coinGeckoId: "regen", @@ -1136,7 +1133,6 @@ export const EmbedChainInfosInit: SimpleMap = { coinGeckoId: "pool:ulum", }, ], - coinType: 118, features: ["ibc-transfer", "ibc-go"], explorerUrlToTx: "https://www.mintscan.io/lum/txs/{txHash}", tmRpc: "https://node0.mainnet.lum.network/rpc/", @@ -1172,7 +1168,6 @@ export const EmbedChainInfosInit: SimpleMap = { coinGeckoId: "vidulum", }, ], - coinType: 370, features: ["ibc-transfer", "ibc-go"], explorerUrlToTx: "https://explorers.vidulum.app/vidulum/tx/{txHash}", tmRpc: "https://mainnet-rpc.vidulum.app/", @@ -1887,7 +1882,6 @@ export const EmbedChainInfosInit: SimpleMap = { coinType: 60, }, bech32Config: IBCAddress.defaultBech32Config("canto"), - coinType: 60, currencies: [ { coinDenom: "CANTO", diff --git a/src/provider/keplr/KeplrAccount.ts b/src/provider/keplr/KeplrAccount.ts index be8996ad..95f5aa95 100644 --- a/src/provider/keplr/KeplrAccount.ts +++ b/src/provider/keplr/KeplrAccount.ts @@ -1,5 +1,5 @@ import { Carbon } from "@carbon-sdk/CarbonSDK"; -import { CARBON_GAS_PRICE, Network, NetworkConfigs, decTypeDecimals } from "@carbon-sdk/constant"; +import { CARBON_GAS_PRICE, CarbonEvmChainIDs, Network, NetworkConfigs, decTypeDecimals } from "@carbon-sdk/constant"; import { CarbonSDK, Models } from "@carbon-sdk/index"; import { AddressUtils, CarbonTx, FetchUtils, NumberUtils } from "@carbon-sdk/util"; import { CarbonSigner, CarbonSignerTypes } from "@carbon-sdk/wallet"; @@ -7,7 +7,7 @@ import { Algo } from "@cosmjs/proto-signing"; import { AppCurrency, ChainInfo, EthSignType, FeeCurrency, Keplr, Key } from "@keplr-wallet/types"; import SDKProvider from "../sdk"; import { ethers } from "ethers"; -import { PUBLIC_KEY_SIGNING_TEXT, populateEvmTransactionDetails } from "@carbon-sdk/util/ethermint"; +import { PUBLIC_KEY_SIGNING_TEXT, parseChainId, populateEvmTransactionDetails } from "@carbon-sdk/util/ethermint"; import { signTransactionWrapper } from "@carbon-sdk/util/provider"; import { parseEvmError } from "../metamask/error"; @@ -203,6 +203,10 @@ class KeplrAccount { rpc: config.tmRpcUrl, chainName: `Carbon (${config.network})`, chainId: chainId, + evm: { + chainId: Number(parseChainId(CarbonEvmChainIDs[config.network])), + rpc: config.evmJsonRpcUrl, + }, bech32Config: { bech32PrefixAccAddr: `${bech32Prefix}`, bech32PrefixAccPub: `${bech32Prefix}pub`, diff --git a/yarn.lock b/yarn.lock index 543ed858..7a3321d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1564,12 +1564,11 @@ comment-json "^4.1.0" find-up "^4.1.0" -"@keplr-wallet/types@^0.11.64": - version "0.11.64" - resolved "https://registry.yarnpkg.com/@keplr-wallet/types/-/types-0.11.64.tgz#5a308c8c019b4e18f894e0f35f0904b60134d605" - integrity sha512-GgzeLDHHfZFyne3O7UIfFHj/uYqVbxAZI31RbBwt460OBbvwQzjrlZwvJW3vieWRAgxKSITjzEDBl2WneFTQdQ== +"@keplr-wallet/types@^0.12.107": + version "0.12.107" + resolved "https://registry.yarnpkg.com/@keplr-wallet/types/-/types-0.12.107.tgz#8d6726d86e17a79131b4b6f4f114052d6384aa58" + integrity sha512-jBpjJO+nNL8cgsJLjZYoq84n+7nXHDdztTgRMVnnomFb+Vy0FVIEI8VUl89ImmHDUImDd0562ywsvA496/0yCA== dependencies: - axios "^0.27.2" long "^4.0.0" "@ledgerhq/devices@^5.51.1": @@ -2450,14 +2449,6 @@ axios@^0.21.2: dependencies: follow-redirects "^1.14.0" -axios@^0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" - integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== - dependencies: - follow-redirects "^1.14.9" - form-data "^4.0.0" - axobject-query@2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9" @@ -3614,11 +3605,6 @@ follow-redirects@^1.10.0, follow-redirects@^1.14.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== -follow-redirects@^1.14.9: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== - form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -3628,15 +3614,6 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - framesync@5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/framesync/-/framesync-5.3.0.tgz#0ecfc955e8f5a6ddc8fdb0cc024070947e1a0d9b" From d636006aed6c355248e6e13eaf8aee30b6011c12 Mon Sep 17 00:00:00 2001 From: Andrew Soon Date: Mon, 1 Jul 2024 12:41:29 +0800 Subject: [PATCH 2/4] Add populate unsigned tx function --- src/provider/keplr/KeplrAccount.ts | 15 ++++---- src/util/ethermint.ts | 58 ++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/src/provider/keplr/KeplrAccount.ts b/src/provider/keplr/KeplrAccount.ts index 95f5aa95..e06a8657 100644 --- a/src/provider/keplr/KeplrAccount.ts +++ b/src/provider/keplr/KeplrAccount.ts @@ -7,7 +7,7 @@ import { Algo } from "@cosmjs/proto-signing"; import { AppCurrency, ChainInfo, EthSignType, FeeCurrency, Keplr, Key } from "@keplr-wallet/types"; import SDKProvider from "../sdk"; import { ethers } from "ethers"; -import { PUBLIC_KEY_SIGNING_TEXT, parseChainId, populateEvmTransactionDetails } from "@carbon-sdk/util/ethermint"; +import { PUBLIC_KEY_SIGNING_TEXT, parseChainId, populateUnsignedEvmTranscation } from "@carbon-sdk/util/ethermint"; import { signTransactionWrapper } from "@carbon-sdk/util/provider"; import { parseEvmError } from "../metamask/error"; @@ -53,16 +53,16 @@ class KeplrAccount { const sendEvmTransaction = async (api: CarbonSDK, req: ethers.providers.TransactionRequest): Promise => { try { - const request = await populateEvmTransactionDetails(api, req) + const unsignedTx = await populateUnsignedEvmTranscation(api, req) const signedTx = await keplr!.signEthereum( // carbon chain id api.wallet?.getChainId() ?? '', // cosmos address api.wallet?.bech32Address ?? '', - JSON.stringify(request), + JSON.stringify(unsignedTx), EthSignType.TRANSACTION, ) - const rlpEncodedHex = `0x${Buffer.from(signedTx).toString('hex')}`; + const rlpEncodedHex = ethers.utils.serializeTransaction(unsignedTx, signedTx) const provider = new ethers.providers.JsonRpcProvider(NetworkConfigs[api.network].evmJsonRpcUrl) return (await provider.sendTransaction(rlpEncodedHex)).hash } @@ -97,16 +97,17 @@ class KeplrAccount { const sendEvmTransaction = async (api: CarbonSDK, req: ethers.providers.TransactionRequest): Promise => { try { - const request = await populateEvmTransactionDetails(api, req) + const unsignedTx = await populateUnsignedEvmTranscation(api, req) const signedTx = await keplr!.signEthereum( // carbon chain id api.wallet?.getChainId() ?? '', // cosmos address api.wallet?.bech32Address ?? '', - JSON.stringify(request), + JSON.stringify(unsignedTx), EthSignType.TRANSACTION, ) - const rlpEncodedHex = `0x${Buffer.from(signedTx).toString('hex')}`; + // const rlpEncodedHex = `0x${Buffer.from(signedTx).toString('hex')}` + const rlpEncodedHex = ethers.utils.serializeTransaction(unsignedTx, signedTx) const provider = new ethers.providers.JsonRpcProvider(NetworkConfigs[api.network].evmJsonRpcUrl) return (await provider.sendTransaction(rlpEncodedHex)).hash } diff --git a/src/util/ethermint.ts b/src/util/ethermint.ts index c383065d..a90b339c 100644 --- a/src/util/ethermint.ts +++ b/src/util/ethermint.ts @@ -78,3 +78,61 @@ export async function populateEvmTransactionDetails(api: CarbonSDK, req: ethers. } } + +export async function populateUnsignedEvmTranscation(api: CarbonSDK, req: ethers.providers.TransactionRequest): Promise { + const provider = new ethers.providers.JsonRpcProvider(NetworkConfigs[api.network].evmJsonRpcUrl) + const evmHexAddress = api.wallet?.evmHexAddress ?? '' + const nonce = req.nonce ?? (await provider.getTransactionCount(evmHexAddress)) + const chainId = req.chainId ?? Number(parseChainId(await api.wallet?.getEvmChainId() ?? '')) + const value = req.value ? `0x${Number(req.value).toString(16)}` : undefined + + const baseTx: ethers.providers.TransactionRequest = { + to: req.to ?? '', + from: req.from ?? api.wallet?.evmHexAddress, + nonce, + data: req.data, + value, + chainId, + // type = 0, 1 or 2 where 0 = legacyTx, 1 = AccessListTx, 2 = DynamicTx. Defaults to DynamicTx + type: req.type ?? 2, + accessList: req.accessList, + } + const gasLimit = (await provider.estimateGas(baseTx)).toHexString() + const gasFee = await provider.getFeeData() + + const unsignedTx: ethers.utils.UnsignedTransaction = { + to: req.to ?? '', + nonce: parseInt(nonce.toString()), + data: req.data, + value, + chainId, + // type = 0, 1 or 2 where 0 = legacyTx, 1 = AccessListTx, 2 = DynamicTx. Defaults to DynamicTx + type: req.type ?? 2, + accessList: req.accessList, + } + + if (!req.gasPrice) { + // Dynamic Tx + return { + ...unsignedTx, + maxPriorityFeePerGas: req.maxPriorityFeePerGas ?? gasFee.maxPriorityFeePerGas?.toHexString(), + maxFeePerGas: req.maxFeePerGas ?? gasFee.maxFeePerGas?.toHexString(), + gasLimit, + type: 2, + } + } + if (req.accessList) { + // AccessList Tx + return { + ...unsignedTx, + gasLimit, + type: 1, + } + } + // LegacyTx + return { + ...unsignedTx, + gasLimit, + type: 0, + } +} \ No newline at end of file From 88c19642298696a1680eb874319cfde9f2d0f35d Mon Sep 17 00:00:00 2001 From: Andrew Soon Date: Mon, 8 Jul 2024 11:33:49 +0800 Subject: [PATCH 3/4] Implement signEthereum for leap --- src/provider/leap/LeapAccount.ts | 61 +++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/src/provider/leap/LeapAccount.ts b/src/provider/leap/LeapAccount.ts index 7cbcdf2d..6e4c3f45 100644 --- a/src/provider/leap/LeapAccount.ts +++ b/src/provider/leap/LeapAccount.ts @@ -1,14 +1,16 @@ import { Carbon } from "@carbon-sdk/CarbonSDK"; -import { CARBON_GAS_PRICE, Network, decTypeDecimals } from "@carbon-sdk/constant"; +import { CARBON_GAS_PRICE, Network, decTypeDecimals, NetworkConfigs } from "@carbon-sdk/constant"; import { CarbonSDK, Models } from "@carbon-sdk/index"; import { AddressUtils, CarbonTx, NumberUtils } from "@carbon-sdk/util"; import { CarbonSigner, CarbonSignerTypes } from "@carbon-sdk/wallet"; import { Algo } from "@cosmjs/proto-signing"; import { Leap } from "@cosmos-kit/leap"; -import { AppCurrency, ChainInfo, FeeCurrency } from "@keplr-wallet/types"; +import { AppCurrency, ChainInfo, EthSignType, FeeCurrency } from "@keplr-wallet/types"; import SDKProvider from "../sdk"; import { ethers } from "ethers"; import { signTransactionWrapper } from "@carbon-sdk/util/provider"; +import { populateUnsignedEvmTranscation } from "@carbon-sdk/util/ethermint"; +import { parseEvmError } from "../metamask/error"; const SWTH: FeeCurrency = { coinDenom: "SWTH", @@ -53,7 +55,31 @@ class LeapAccount { }; const sendEvmTransaction = async (api: CarbonSDK, req: ethers.providers.TransactionRequest) => { // eslint-disable-line - throw new Error("signing not available"); + try { + // workaround for version mismatch in cosmos-kit/leap and leap-extension + const leapInterface = leap as any + if (typeof (leapInterface as any).signEthereum === 'function') { + + const unsignedTx = await populateUnsignedEvmTranscation(api, req) + const signedTx = await leapInterface!.signEthereum( + // carbon chain id + api.wallet?.getChainId() ?? '', + // cosmos address + api.wallet?.bech32Address ?? '', + JSON.stringify(unsignedTx), + EthSignType.TRANSACTION, + ) as Uint8Array + const rlpEncodedHex = ethers.utils.serializeTransaction(unsignedTx, signedTx) + const provider = new ethers.providers.JsonRpcProvider(NetworkConfigs[api.network].evmJsonRpcUrl) + return (await provider.sendTransaction(rlpEncodedHex)).hash + } else { + throw new Error("signing not available") + } + } + catch (error) { + console.error(error) + throw (parseEvmError(error as Error)) + } } return { @@ -84,8 +110,33 @@ class LeapAccount { ]; }; + const sendEvmTransaction = async (api: CarbonSDK, req: ethers.providers.TransactionRequest) => { // eslint-disable-line - throw new Error("signing not available"); + try { + const leapInterface = leap as any + if (typeof (leapInterface as any).signEthereum === 'function') { + + const unsignedTx = await populateUnsignedEvmTranscation(api, req) + console.log('xx unsignedTx', unsignedTx) + const signedTx = await leapInterface!.signEthereum( + // carbon chain id + api.wallet?.getChainId() ?? '', + // cosmos address + api.wallet?.bech32Address ?? '', + JSON.stringify(unsignedTx), + EthSignType.TRANSACTION, + ) as Uint8Array + const rlpEncodedHex = ethers.utils.serializeTransaction(unsignedTx, signedTx) + const provider = new ethers.providers.JsonRpcProvider(NetworkConfigs[api.network].evmJsonRpcUrl) + return (await provider.sendTransaction(rlpEncodedHex)).hash + } else { + throw new Error("signing not available") + } + } + catch (error) { + console.error(error) + throw (parseEvmError(error as Error)) + } } return { @@ -154,7 +205,7 @@ class LeapAccount { } } -namespace LeapAccount {} +namespace LeapAccount { } export interface LeapExtended extends Leap { experimentalSuggestChain(chainInfo: ChainInfo): Promise; From abb02f79d9e9bc9c22ced275f63ca206ed309ae3 Mon Sep 17 00:00:00 2001 From: Randy Date: Wed, 10 Jul 2024 12:36:56 +0800 Subject: [PATCH 4/4] v0.11.3-beta.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 68f4aedc..2abf30b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "carbon-js-sdk", - "version": "0.11.2", + "version": "0.11.3-beta.1", "description": "TypeScript SDK for Carbon blockchain", "main": "lib/index.js", "types": "lib/index.d.ts",