diff --git a/packages/sdk-ts/src/core/exports.ts b/packages/sdk-ts/src/core/exports.ts deleted file mode 100644 index f155c3c7c..000000000 --- a/packages/sdk-ts/src/core/exports.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * as ExchangeCore from './exchange' -export * as AuctionCore from './auction' -export * as AuthzCore from './authz' -export * as BankCore from './bank' -export * as DistributionCore from './distribution' -export * as GovCore from './gov' -export * as IbcCore from './ibc' -export * as InsuranceCore from './insurance' -export * as PeggyCore from './peggy' -export * as StakingCore from './staking' diff --git a/packages/sdk-ui-ts/src/core/Web3Client.ts b/packages/sdk-ui-ts/src/core/Web3Client.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/sdk-ui-ts/src/services/erc20.ts b/packages/sdk-ui-ts/src/services/erc20.ts deleted file mode 100644 index 93ee4c636..000000000 --- a/packages/sdk-ui-ts/src/services/erc20.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { createAlchemyWeb3 } from '@alch/alchemy-web3' -import { Network } from '@injectivelabs/networks' -import { EthereumChainId, TransactionOptions } from '@injectivelabs/ts-types' -import { - Erc20Contract, - getContractAddressesForNetworkOrThrow, - PeggyContract, -} from '@injectivelabs/contracts' -import { - GAS_LIMIT_MULTIPLIER, - INJ_DENOM, - TX_DEFAULTS_GAS, - ZERO_IN_BASE, -} from '../constants' -import { BigNumberInWei, DEFAULT_GAS_PRICE } from '@injectivelabs/utils' -import { getAddressFromInjectiveAddress } from '@injectivelabs/sdk-ts/dist/utils' -import { Token, TokenWithBalance } from '../types' - -export const getTransactionOptions = ( - transactionOptions: Partial, -): TransactionOptions => ({ - from: transactionOptions.from, - gas: transactionOptions.gas ? transactionOptions.gas : TX_DEFAULTS_GAS, - gasPrice: transactionOptions.gasPrice - ? transactionOptions.gasPrice.toString() - : DEFAULT_GAS_PRICE.toString(), -}) - -export const peggyDenomToContractAddress = ( - denom: string, - injectiveContractAddress: string, -): string => { - const denomLowerCased = denom.toLowerCase() - const contractAddress = denomLowerCased.replace('peggy', '') - - return denomLowerCased === INJ_DENOM - ? injectiveContractAddress - : contractAddress -} - -export const getSetTokenAllowanceTx = async ({ - address, - amount, - rpc, - network, - ethereumChainId, - gasPrice, - tokenAddress, -}: { - address: string - rpc: string - network: Network - ethereumChainId: EthereumChainId - amount: string - gasPrice: string - tokenAddress: string -}) => { - const web3 = createAlchemyWeb3(rpc) - const erc20Contract = new Erc20Contract({ - ethereumChainId, - web3: web3 as any, - address: tokenAddress, - }) - const contractAddresses = getContractAddressesForNetworkOrThrow(network) - const setAllowanceOfContractFunction = erc20Contract.setAllowanceOf({ - amount, - contractAddress: contractAddresses.peggy, - transactionOptions: getTransactionOptions({ - gasPrice: ZERO_IN_BASE.toFixed(), - from: address, - }), - }) - - const data = setAllowanceOfContractFunction.getABIEncodedTransactionData() - const gas = new BigNumberInWei( - await setAllowanceOfContractFunction.estimateGasAsync(), - ) - - return { - from: address, - to: tokenAddress, - gas: new BigNumberInWei(gas.times(GAS_LIMIT_MULTIPLIER).toFixed(0)) - .toNumber() - .toString(16), - maxFeePerGas: new BigNumberInWei(gasPrice).toNumber().toString(16), - maxPriorityFeePerGas: null, - data, - } -} - -export const getPeggyTransferTx = async ({ - address, - amount, - network, - ethereumChainId, - denom, - rpc, - destinationAddress, - gasPrice, -}: { - network: Network - ethereumChainId: EthereumChainId - address: string - rpc: string - amount: string // BigNumberInWi - denom: string - destinationAddress: string - gasPrice: string // BigNumberInWei -}) => { - const web3 = createAlchemyWeb3(rpc) - const contractAddresses = getContractAddressesForNetworkOrThrow(network) - const contractAddress = peggyDenomToContractAddress( - denom, - contractAddresses.injective, - ) - const peggyContractAddress = contractAddresses.peggy - const contract = new PeggyContract({ - address: peggyContractAddress, - ethereumChainId: ethereumChainId, - web3: web3 as any, - }) - const formattedDestinationAddress = - getAddressFromInjectiveAddress(destinationAddress) - - const depositForContractFunction = contract.sendToCosmos({ - contractAddress, - amount: new BigNumberInWei(amount).toFixed(), - address: `0x${'0'.repeat(24)}${formattedDestinationAddress.slice(2)}`, - transactionOptions: getTransactionOptions({ - gasPrice: ZERO_IN_BASE.toFixed(), - from: address, - }), - }) - - const data = depositForContractFunction.getABIEncodedTransactionData() - const gas = new BigNumberInWei( - await depositForContractFunction.estimateGasAsync(), - ) - - return { - from: address, - to: peggyContractAddress, - gas: new BigNumberInWei(gas.times(GAS_LIMIT_MULTIPLIER).toFixed(0)) - .toNumber() - .toString(16), - maxFeePerGas: new BigNumberInWei(gasPrice).toNumber().toString(16), - maxPriorityFeePerGas: null, - data, - } -} - -export const fetchTokenBalanceAndAllowance = async ({ - address, - token, - network, - rpc, -}: { - address: string - token: Token - network: Network - rpc: string -}): Promise => { - if (token.denom.startsWith('ibc/')) { - return { - ...token, - balance: new BigNumberInWei(0).toFixed(), - allowance: new BigNumberInWei(0).toFixed(), - } - } - - try { - const web3 = createAlchemyWeb3(rpc) - const tokenBalances = await web3.alchemy.getTokenBalances(address, [ - token.address, - ]) - const tokenBalance = tokenBalances.tokenBalances - .filter((tokenBalance) => tokenBalance.tokenBalance) - .find( - (tokenBalance) => - ( - tokenBalance as unknown as { contractAddress: string } - ).contractAddress.toLowerCase() === token.address.toLowerCase(), - ) - const balance = tokenBalance ? tokenBalance.tokenBalance || 0 : 0 - - const contractAddresses = getContractAddressesForNetworkOrThrow(network) - const allowance = await web3.alchemy.getTokenAllowance({ - owner: address, - spender: contractAddresses.peggy, - contract: token.address, - }) - - return { - ...token, - balance: new BigNumberInWei(balance || 0).toFixed(), - allowance: new BigNumberInWei(allowance || 0).toFixed(), - } - } catch (e) { - return { - ...token, - balance: new BigNumberInWei(0).toFixed(), - allowance: new BigNumberInWei(0).toFixed(), - } - } -} diff --git a/packages/sdk-ui-ts/src/services/index.ts b/packages/sdk-ui-ts/src/services/index.ts index 598100492..1a5eaa6e1 100644 --- a/packages/sdk-ui-ts/src/services/index.ts +++ b/packages/sdk-ui-ts/src/services/index.ts @@ -1,2 +1 @@ export * from './gas' -export * from './erc20' diff --git a/packages/wallet-ts/package.json b/packages/wallet-ts/package.json index 51a0a6985..ce8541d16 100644 --- a/packages/wallet-ts/package.json +++ b/packages/wallet-ts/package.json @@ -36,7 +36,9 @@ "@ethereumjs/common": "^2.6.3", "@ethereumjs/tx": "^3.5.1", "@injectivelabs/chain-api": "^1.8.0-rc1", + "@injectivelabs/contracts": "^0.5.27", "@injectivelabs/exceptions": "^0.5.4", + "@injectivelabs/networks": "^0.5.9", "@injectivelabs/ts-types": "^0.5.3", "@injectivelabs/utils": "^0.5.6", "@keplr-wallet/cosmos": "^0.10.5", diff --git a/packages/wallet-ts/src/Cosmos/CosmosQuery.ts b/packages/wallet-ts/src/Cosmos/CosmosQuery.ts index 99d3a36dd..c4d67ee5c 100644 --- a/packages/wallet-ts/src/Cosmos/CosmosQuery.ts +++ b/packages/wallet-ts/src/Cosmos/CosmosQuery.ts @@ -5,7 +5,7 @@ import { BankBalancesRestResponse, BlockLatestRestResponse, NodeInfoRestResponse, -} from '../types/lcd' +} from './types' export class CosmosQuery { private rest: string diff --git a/packages/wallet-ts/src/Cosmos/index.ts b/packages/wallet-ts/src/Cosmos/index.ts index 0cac198f4..46ef88a26 100644 --- a/packages/wallet-ts/src/Cosmos/index.ts +++ b/packages/wallet-ts/src/Cosmos/index.ts @@ -1,3 +1,4 @@ export * from './CosmosQuery' export * from './CosmosWallet' export * from './utils' +export * from './types' diff --git a/packages/wallet-ts/src/types/lcd.ts b/packages/wallet-ts/src/Cosmos/types.ts similarity index 100% rename from packages/wallet-ts/src/types/lcd.ts rename to packages/wallet-ts/src/Cosmos/types.ts diff --git a/packages/wallet-ts/src/wallet-strategy/WalletStrategy.ts b/packages/wallet-ts/src/wallet-strategy/WalletStrategy.ts index 0888d2a32..f652cbce0 100644 --- a/packages/wallet-ts/src/wallet-strategy/WalletStrategy.ts +++ b/packages/wallet-ts/src/wallet-strategy/WalletStrategy.ts @@ -1,5 +1,9 @@ import Web3 from 'web3' -import { AccountAddress, ChainId } from '@injectivelabs/ts-types' +import { + AccountAddress, + ChainId, + EthereumChainId, +} from '@injectivelabs/ts-types' import { createAlchemyWeb3 } from '@alch/alchemy-web3' import Metamask from './strategies/Metamask' import { @@ -105,6 +109,13 @@ export default class WalletStrategy { return this.getStrategy().sendTransaction(tx, options) } + public async sendEthereumTransaction( + tx: any, + options: { address: AccountAddress; ethereumChainId: EthereumChainId }, + ): Promise { + return this.getStrategy().sendEthereumTransaction(tx, options) + } + public async signTransaction( data: any, address: AccountAddress, diff --git a/packages/wallet-ts/src/web3/Web3Client.ts b/packages/wallet-ts/src/web3/Web3Client.ts new file mode 100644 index 000000000..f7a57b974 --- /dev/null +++ b/packages/wallet-ts/src/web3/Web3Client.ts @@ -0,0 +1,215 @@ +import { EthereumChainId, TransactionOptions } from '@injectivelabs/ts-types' +import { Network } from '@injectivelabs/networks' +import { BigNumberInWei } from '@injectivelabs/utils' +import { + Erc20Contract, + PeggyContract, + getContractAddressesForNetworkOrThrow, +} from '@injectivelabs/contracts' +import { WalletStrategy } from '../wallet-strategy' +import { SendTransactionOptions } from './types' +import { + DEFAULT_GAS_PRICE, + GAS_LIMIT_MULTIPLIER, + INJ_DENOM, + TX_DEFAULTS_GAS, +} from './constants' + +export const getTransactionOptions = ( + transactionOptions: Partial, +): TransactionOptions => ({ + from: transactionOptions.from, + gas: transactionOptions.gas ? transactionOptions.gas : TX_DEFAULTS_GAS, + gasPrice: transactionOptions.gasPrice + ? transactionOptions.gasPrice.toString() + : DEFAULT_GAS_PRICE.toString(), +}) + +export const peggyDenomToContractAddress = ( + denom: string, + injectiveContractAddress: string, +): string => { + const denomLowerCased = denom.toLowerCase() + const contractAddress = denomLowerCased.replace('peggy', '') + + return denomLowerCased === INJ_DENOM + ? injectiveContractAddress + : contractAddress +} + +export class Web3Client { + private walletStrategy: WalletStrategy + + constructor(walletStrategy: WalletStrategy) { + this.walletStrategy = walletStrategy + } + + async signTransaction(args: SendTransactionOptions) { + const { walletStrategy } = this + + const txHash = await walletStrategy.sendEthereumTransaction(args.tx, { + address: args.address, + ethereumChainId: args.ethereumChainId, + }) + + await walletStrategy.getEthereumTransactionReceipt(txHash) + } + + async getSetTokenAllowanceTx({ + address, + amount, + network, + ethereumChainId, + gasPrice, + tokenAddress, + }: { + address: string + network: Network + ethereumChainId: EthereumChainId + amount: string + gasPrice: string + tokenAddress: string + }) { + const { walletStrategy } = this + const web3 = walletStrategy.getWeb3() as any + const erc20Contract = new Erc20Contract({ + ethereumChainId, + web3: web3 as any, + address: tokenAddress, + }) + const contractAddresses = getContractAddressesForNetworkOrThrow(network) + const setAllowanceOfContractFunction = erc20Contract.setAllowanceOf({ + amount, + contractAddress: contractAddresses.peggy, + transactionOptions: getTransactionOptions({ + gasPrice: '0', + from: address, + }), + }) + + const data = setAllowanceOfContractFunction.getABIEncodedTransactionData() + const gas = new BigNumberInWei( + await setAllowanceOfContractFunction.estimateGasAsync(), + ) + + return { + from: address, + to: tokenAddress, + gas: new BigNumberInWei(gas.times(GAS_LIMIT_MULTIPLIER).toFixed(0)) + .toNumber() + .toString(16), + maxFeePerGas: new BigNumberInWei(gasPrice).toNumber().toString(16), + maxPriorityFeePerGas: null, + data, + } + } + + async getPeggyTransferTx({ + address, + amount, + network, + ethereumChainId, + denom, + destinationAddress, + gasPrice, + }: { + network: Network + ethereumChainId: EthereumChainId + address: string + amount: string // BigNumberInWi + denom: string + destinationAddress: string + gasPrice: string // BigNumberInWei + }) { + const { walletStrategy } = this + const web3 = walletStrategy.getWeb3() as any + const contractAddresses = getContractAddressesForNetworkOrThrow(network) + const contractAddress = peggyDenomToContractAddress( + denom, + contractAddresses.injective, + ) + const peggyContractAddress = contractAddresses.peggy + const contract = new PeggyContract({ + address: peggyContractAddress, + ethereumChainId, + web3: web3 as any, + }) + + const depositForContractFunction = contract.sendToCosmos({ + contractAddress, + amount: new BigNumberInWei(amount).toFixed(), + address: `0x${'0'.repeat(24)}${destinationAddress.slice(2)}`, + transactionOptions: getTransactionOptions({ + gasPrice: '0', + from: address, + }), + }) + + const data = depositForContractFunction.getABIEncodedTransactionData() + const gas = new BigNumberInWei( + await depositForContractFunction.estimateGasAsync(), + ) + + return { + from: address, + to: peggyContractAddress, + gas: new BigNumberInWei(gas.times(GAS_LIMIT_MULTIPLIER).toFixed(0)) + .toNumber() + .toString(16), + maxFeePerGas: new BigNumberInWei(gasPrice).toNumber().toString(16), + maxPriorityFeePerGas: null, + data, + } + } + + async fetchTokenBalanceAndAllowance({ + address, + contractAddress, + network, + }: { + address: string + contractAddress: string + network: Network + }): Promise<{ balance: string; allowance: string }> { + if (contractAddress.startsWith('ibc/')) { + return { + balance: new BigNumberInWei(0).toFixed(), + allowance: new BigNumberInWei(0).toFixed(), + } + } + + try { + const { walletStrategy } = this + const web3 = walletStrategy.getWeb3() as any + const tokenBalances = await web3.alchemy.getTokenBalances(address, [ + contractAddress, + ]) + const tokenBalance = tokenBalances.tokenBalances + .filter((tokenBalance: any) => tokenBalance.tokenBalance) + .find( + (tokenBalance: any) => + ( + tokenBalance as unknown as { contractAddress: string } + ).contractAddress.toLowerCase() === contractAddress.toLowerCase(), + ) + const balance = tokenBalance ? tokenBalance.tokenBalance || 0 : 0 + + const contractAddresses = getContractAddressesForNetworkOrThrow(network) + const allowance = await web3.alchemy.getTokenAllowance({ + owner: address, + spender: contractAddresses.peggy, + contract: contractAddress, + }) + + return { + balance: new BigNumberInWei(balance || 0).toFixed(), + allowance: new BigNumberInWei(allowance || 0).toFixed(), + } + } catch (e) { + return { + balance: new BigNumberInWei(0).toFixed(), + allowance: new BigNumberInWei(0).toFixed(), + } + } + } +} diff --git a/packages/wallet-ts/src/web3/constants.ts b/packages/wallet-ts/src/web3/constants.ts new file mode 100644 index 000000000..b204423fc --- /dev/null +++ b/packages/wallet-ts/src/web3/constants.ts @@ -0,0 +1,7 @@ +import { BigNumberInBase } from '@injectivelabs/utils' + +export const GWEI_IN_WEI: BigNumberInBase = new BigNumberInBase(1000000000) +export const GAS_LIMIT_MULTIPLIER = 1.2 +export const TX_DEFAULTS_GAS = 80_000_000 +export const DEFAULT_GAS_PRICE = new BigNumberInBase(6).times(GWEI_IN_WEI) +export const INJ_DENOM = 'inj' diff --git a/packages/wallet-ts/src/web3/index.ts b/packages/wallet-ts/src/web3/index.ts new file mode 100644 index 000000000..0d85fd719 --- /dev/null +++ b/packages/wallet-ts/src/web3/index.ts @@ -0,0 +1,2 @@ +export * from './Web3Client' +export * from './types' diff --git a/packages/wallet-ts/src/web3/types.ts b/packages/wallet-ts/src/web3/types.ts new file mode 100644 index 000000000..e1bf15afa --- /dev/null +++ b/packages/wallet-ts/src/web3/types.ts @@ -0,0 +1,14 @@ +import { EthereumChainId } from '@injectivelabs/ts-types' + +export interface SendTransactionOptions { + tx: { + from: string + to: string + gas: string + maxFeePerGas: string + maxPriorityFeePerGas: string | null + data: any + } + address: string + ethereumChainId: EthereumChainId +} diff --git a/packages/wallet-ts/tsconfig.build.json b/packages/wallet-ts/tsconfig.build.json index 1717d6d8b..98b3450f0 100644 --- a/packages/wallet-ts/tsconfig.build.json +++ b/packages/wallet-ts/tsconfig.build.json @@ -15,6 +15,12 @@ { "path": "../exceptions/tsconfig.build.json" }, + { + "path": "../networks/tsconfig.build.json" + }, + { + "path": "../contracts/tsconfig.build.json" + }, { "path": "../utils/tsconfig.build.json" }, diff --git a/packages/wallet-ts/tsconfig.json b/packages/wallet-ts/tsconfig.json index 9238f46a4..5b679e469 100644 --- a/packages/wallet-ts/tsconfig.json +++ b/packages/wallet-ts/tsconfig.json @@ -7,6 +7,12 @@ { "path": "../exceptions/tsconfig.json" }, + { + "path": "../networks/tsconfig.json" + }, + { + "path": "../contracts/tsconfig.json" + }, { "path": "../utils/tsconfig.json" },