diff --git a/package.json b/package.json index 3677a430..f290c2d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "juneojs", - "version": "0.0.97", + "version": "0.0.98", "description": "Juneo JS Library", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/api/data.ts b/src/api/data.ts index 507877d4..d68f664b 100644 --- a/src/api/data.ts +++ b/src/api/data.ts @@ -1,4 +1,4 @@ -import { now } from '../utils' +import { TimeUtils } from '../utils' import { type JsonRpcRequest, type JsonRpcResponse, type JuneoClient } from './client' export class CachedResponse { @@ -30,7 +30,7 @@ export class CachedResponse { if (this.duration === undefined) { return false } - const currentTime: bigint = now() + const currentTime: bigint = TimeUtils.now() const update: boolean = currentTime >= this.lastUpdate + this.duration if (update) { this.lastUpdate = currentTime diff --git a/src/chain/chain.ts b/src/chain/chain.ts index 3c4bb9ed..f7f63846 100644 --- a/src/chain/chain.ts +++ b/src/chain/chain.ts @@ -1,16 +1,10 @@ import { type TokenAsset } from '../asset' -import { type MCNProvider } from '../juneo' - -export enum VMAccountType { - Utxo = 'Utxo', - Nonce = 'Nonce', -} +import { type ChainVM, type MCNProvider } from '../juneo' export interface Blockchain { name: string id: string - vmId: string - accountType: VMAccountType + vm: ChainVM asset: TokenAsset assetId: string aliases: string[] @@ -27,8 +21,7 @@ export interface Blockchain { export abstract class AbstractBlockchain implements Blockchain { name: string id: string - vmId: string - accountType: VMAccountType + vm: ChainVM asset: TokenAsset assetId: string aliases: string[] @@ -37,16 +30,14 @@ export abstract class AbstractBlockchain implements Blockchain { constructor ( name: string, id: string, - vmId: string, - accountType: VMAccountType, + vm: ChainVM, asset: TokenAsset, aliases: string[] = [], registeredAssets: TokenAsset[] = [] ) { this.name = name this.id = id - this.vmId = vmId - this.accountType = accountType + this.vm = vm this.asset = asset this.assetId = asset.assetId this.aliases = aliases diff --git a/src/chain/index.ts b/src/chain/index.ts index 6f365e4b..e2e3241d 100644 --- a/src/chain/index.ts +++ b/src/chain/index.ts @@ -3,3 +3,4 @@ export * from './jevm' export * from './jvm' export * from './platform' export * from './solidity' +export * from './vm' diff --git a/src/chain/jevm.ts b/src/chain/jevm.ts index 81af6c80..887e9c7b 100644 --- a/src/chain/jevm.ts +++ b/src/chain/jevm.ts @@ -1,15 +1,18 @@ import { ethers } from 'ethers' -import { ChainError, fetchJNT, isContractAddress } from '../utils' -import { AssetId } from '../transaction' import { type JEVMAPI } from '../api' -import { ERC20TokenHandler, ContractManager, type SolidityTokenHandler } from './solidity' -import { type TokenAsset, type JRC20Asset, type JEVMGasToken, TokenType, type WrappedAsset } from '../asset' -import { AbstractBlockchain, VMAccountType } from './chain' +import { type JEVMGasToken, type JRC20Asset, type TokenAsset, TokenType, type WrappedAsset } from '../asset' import { type MCNProvider } from '../juneo' +import { AssetId } from '../transaction' +import { ChainError, fetchJNT, isContractAddress } from '../utils' +import { AbstractBlockchain } from './chain' +import { ContractManager, ERC20TokenHandler, type SolidityTokenHandler } from './solidity' +import { ChainVM, VMWalletType } from './vm' export const JEVM_ID: string = 'orkbbNQVf27TiBe6GqN5dm8d8Lo3rutEov8DUWZaKNUjckwSk' export const EVM_ID: string = 'mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6' +const HD_PATH = 60 + export const NativeAssetBalanceContract: string = '0x0100000000000000000000000000000000000001' export const NativeAssetCallContract: string = '0x0100000000000000000000000000000000000002' @@ -37,7 +40,7 @@ export class JEVMBlockchain extends AbstractBlockchain { jrc20Assets: JRC20Asset[] = [], wrappedAsset?: WrappedAsset | undefined ) { - super(name, id, JEVM_ID, VMAccountType.Nonce, asset, aliases, registeredAssets) + super(name, id, new ChainVM(JEVM_ID, VMWalletType.Nonce, HD_PATH), asset, aliases, registeredAssets) this.asset = asset this.chainId = chainId this.baseFee = baseFee diff --git a/src/chain/jvm.ts b/src/chain/jvm.ts index fe8efc8c..9eced5c5 100644 --- a/src/chain/jvm.ts +++ b/src/chain/jvm.ts @@ -1,13 +1,16 @@ -import { fetchJNT, validateBech32 } from '../utils' -import { type TokenAsset, type JNTAsset } from '../asset' -import { AbstractBlockchain, VMAccountType } from './chain' +import { type JNTAsset, type TokenAsset } from '../asset' import { type MCNProvider } from '../juneo' +import { fetchJNT, validateBech32 } from '../utils' +import { AbstractBlockchain } from './chain' +import { ChainVM, VMWalletType } from './vm' export const JVM_ID: string = 'otSmSxFRBqdRX7kestRW732n3WS2MrLAoWwHZxHnmMGMuLYX8' +const HD_PATH = 9000 + export class JVMBlockchain extends AbstractBlockchain { constructor (name: string, id: string, asset: JNTAsset, aliases?: string[], registeredAssets: TokenAsset[] = []) { - super(name, id, JVM_ID, VMAccountType.Utxo, asset, aliases, registeredAssets) + super(name, id, new ChainVM(JVM_ID, VMWalletType.Utxo, HD_PATH), asset, aliases, registeredAssets) } protected async fetchAsset (provider: MCNProvider, assetId: string): Promise { diff --git a/src/chain/platform.ts b/src/chain/platform.ts index 7f1e0651..fe3ebc9e 100644 --- a/src/chain/platform.ts +++ b/src/chain/platform.ts @@ -1,10 +1,13 @@ -import { fetchJNT, now, RewardCalculator, validateBech32 } from '../utils' -import { type TokenAsset, type JNTAsset } from '../asset' -import { AbstractBlockchain, VMAccountType } from './chain' +import { type JNTAsset, type TokenAsset } from '../asset' import { type MCNProvider } from '../juneo' +import { fetchJNT, RewardCalculator, TimeUtils, validateBech32 } from '../utils' +import { AbstractBlockchain } from './chain' +import { ChainVM, VMWalletType } from './vm' export const PLATFORMVM_ID: string = '11111111111111111111111111111111LpoYY' +const HD_PATH = 9000 + const BaseShare: number = 100_0000 // 100% export class PlatformBlockchain extends AbstractBlockchain { @@ -21,7 +24,7 @@ export class PlatformBlockchain extends AbstractBlockchain { aliases?: string[], registeredAssets: TokenAsset[] = [] ) { - super(name, id, PLATFORMVM_ID, VMAccountType.Utxo, asset, aliases, registeredAssets) + super(name, id, new ChainVM(PLATFORMVM_ID, VMWalletType.Utxo, HD_PATH), asset, aliases, registeredAssets) this.stakeConfig = stakeConfig this.rewardConfig = rewardConfig this.rewardCalculator = new RewardCalculator(rewardConfig) @@ -36,11 +39,11 @@ export class PlatformBlockchain extends AbstractBlockchain { } estimatePrimaryValidationReward (stakePeriod: bigint, stakeAmount: bigint): bigint { - return this.rewardCalculator.calculatePrimary(stakePeriod, now(), stakeAmount) + return this.rewardCalculator.calculatePrimary(stakePeriod, TimeUtils.now(), stakeAmount) } estimatePrimaryDelegationReward (stakePeriod: bigint, stakeAmount: bigint): bigint { - const rewards: bigint = this.rewardCalculator.calculatePrimary(stakePeriod, now(), stakeAmount) + const rewards: bigint = this.rewardCalculator.calculatePrimary(stakePeriod, TimeUtils.now(), stakeAmount) return (rewards * BigInt(BaseShare - this.stakeConfig.minDelegationFee)) / BigInt(BaseShare) } } diff --git a/src/chain/solidity/abi.ts b/src/chain/solidity/abi.ts index 7353407a..5aa0a2ad 100644 --- a/src/chain/solidity/abi.ts +++ b/src/chain/solidity/abi.ts @@ -29,10 +29,3 @@ export const WrappedABI: string[] = ERC20ABI.concat([ 'event Deposit(address indexed dst, uint wad)', 'event Withdrawal(address indexed src, uint wad)' ]) - -export const AuctionABI: string[] = ['function redeemAuction(uint256 auctionId)'] - -export const StreamABI: string[] = [ - 'function withdrawFromStream(uint256 streamId, uint256 amount) returns (bool)', - 'function cancelStream(uint256 streamId) returns (bool)' -] diff --git a/src/chain/solidity/contract.ts b/src/chain/solidity/contract.ts index b3ce3a64..1aa9881a 100644 --- a/src/chain/solidity/contract.ts +++ b/src/chain/solidity/contract.ts @@ -1,7 +1,7 @@ import { ethers } from 'ethers' -import * as abi from './abi' -import { AssetId } from '../../transaction' import { ERC20Asset, type TokenAsset } from '../../asset' +import { AssetId } from '../../transaction' +import * as abi from './abi' export class ContractManager { private readonly handlers: SolidityTokenHandler[] = [] @@ -120,27 +120,3 @@ export class WrappedContractAdapter extends EVMCallAdapter { return this.getFunctionData('deposit') } } - -export class AuctionContractAdapter extends EVMCallAdapter { - constructor (contractAddress: string) { - super(contractAddress, abi.AuctionABI) - } - - getRedeemAuctionData (auctionId: bigint): string { - return this.getFunctionData('redeemAuction', [auctionId]) - } -} - -export class StreamContractAdapter extends EVMCallAdapter { - constructor (contractAddress: string) { - super(contractAddress, abi.StreamABI) - } - - getWithdrawFromStreamData (streamId: bigint, amount: bigint): string { - return this.getFunctionData('withdrawFromStream', [streamId, amount]) - } - - getCancelStreamData (streamId: bigint): string { - return this.getFunctionData('cancelStream', [streamId]) - } -} diff --git a/src/chain/vm.ts b/src/chain/vm.ts new file mode 100644 index 00000000..6461707c --- /dev/null +++ b/src/chain/vm.ts @@ -0,0 +1,16 @@ +export enum VMWalletType { + Utxo = 'Utxo', + Nonce = 'Nonce', +} + +export class ChainVM { + id: string + walletType: VMWalletType + hdPath: number + + constructor (id: string, walletType: VMWalletType, hdPath: number) { + this.id = id + this.walletType = walletType + this.hdPath = hdPath + } +} diff --git a/src/juneo.ts b/src/juneo.ts index 60fcb3ab..8f4600fa 100644 --- a/src/juneo.ts +++ b/src/juneo.ts @@ -27,7 +27,7 @@ export class MCNProvider { this.juneApi = new JEVMAPI(client, this.juneChain) for (const supernet of this.mcn.supernets) { for (const chain of supernet.chains) { - if (chain.vmId === JEVM_ID) { + if (chain.vm.id === JEVM_ID) { this.jevmApi[chain.id] = new JEVMAPI(client, chain as JEVMBlockchain) } } diff --git a/src/transaction/builder.ts b/src/transaction/builder.ts index 726ac30b..307cc443 100644 --- a/src/transaction/builder.ts +++ b/src/transaction/builder.ts @@ -1,9 +1,8 @@ -import { InputError, OutputError } from '../utils' +import { InputError, OutputError, TimeUtils } from '../utils' import { Secp256k1Input, type Spendable, TransferableInput, type UserInput } from './input' import { Secp256k1Output, Secp256k1OutputTypeId, type TransactionOutput, UserOutput, type Utxo } from './output' -import * as time from '../utils/time' -import { Address, AssetId } from './types' import { type TransactionFee } from './transaction' +import { Address, AssetId } from './types' export function buildTransactionInputs ( userInputs: UserInput[], @@ -47,7 +46,7 @@ export function buildTransactionInputs ( } const output: Secp256k1Output = utxo.output as Secp256k1Output // output cannot be consumed because it is timelocked - if (output.locktime > time.now()) { + if (output.locktime > TimeUtils.now()) { continue } // The utxo will be added as an input in any case diff --git a/src/transaction/jevm/status.ts b/src/transaction/jevm/status.ts index 93e2d77b..4f9b5a82 100644 --- a/src/transaction/jevm/status.ts +++ b/src/transaction/jevm/status.ts @@ -1,5 +1,5 @@ import { type JEVMAPI } from '../../api' -import { sleep } from '../../utils' +import { TimeUtils } from '../../utils' import { type TransactionStatusFetcher, TransactionStatusFetchDelay } from '../transaction' export enum JEVMTransactionStatus { @@ -23,7 +23,7 @@ export class JEVMTransactionStatusFetcher implements TransactionStatusFetcher { async fetch (timeout: number, delay: number = TransactionStatusFetchDelay): Promise { const maxAttempts: number = timeout / delay while (this.attempts < maxAttempts && !this.isCurrentStatusSettled()) { - await sleep(delay) + await TimeUtils.sleep(delay) await this.jevmApi.getTxStatus(this.transactionId).then( (value) => { this.currentStatus = value.status @@ -68,7 +68,7 @@ export class EVMTransactionStatusFetcher implements TransactionStatusFetcher { const maxAttempts: number = timeout / delay this.currentStatus = EVMTransactionStatus.Pending while (this.attempts < maxAttempts && !this.isCurrentStatusSettled()) { - await sleep(delay) + await TimeUtils.sleep(delay) const receipt: any = await this.jevmApi.eth_getTransactionReceipt(this.transactionHash).catch(() => { return null }) diff --git a/src/transaction/jvm/status.ts b/src/transaction/jvm/status.ts index 06f4fd9c..a659bfc8 100644 --- a/src/transaction/jvm/status.ts +++ b/src/transaction/jvm/status.ts @@ -1,5 +1,5 @@ import { type JVMAPI } from '../../api' -import { sleep } from '../../utils' +import { TimeUtils } from '../../utils' import { type TransactionStatusFetcher, TransactionStatusFetchDelay } from '../transaction' export enum JVMTransactionStatus { @@ -23,7 +23,7 @@ export class JVMTransactionStatusFetcher implements TransactionStatusFetcher { const maxAttempts: number = timeout / delay this.currentStatus = JVMTransactionStatus.Processing while (this.attempts < maxAttempts && !this.isCurrentStatusSettled()) { - await sleep(delay) + await TimeUtils.sleep(delay) await this.jvmApi.getTx(this.transactionId).then( () => { this.currentStatus = JVMTransactionStatus.Accepted diff --git a/src/transaction/platform/builder.ts b/src/transaction/platform/builder.ts index bec19c5d..9c7368b0 100644 --- a/src/transaction/platform/builder.ts +++ b/src/transaction/platform/builder.ts @@ -2,10 +2,10 @@ import { type PlatformBlockchain } from '../../chain' import { InputError } from '../../utils' import { buildTransactionInputs, buildTransactionOutputs } from '../builder' import { UserInput, type TransferableInput } from '../input' -import { type UserOutput, TransferableOutput, Secp256k1Output, type Utxo } from '../output' +import { Secp256k1Output, TransferableOutput, type UserOutput, type Utxo } from '../output' import { TransactionFee } from '../transaction' import { Address, AssetId, BlockchainId, DynamicId, NodeId, SupernetId } from '../types' -import { type SupernetAuth, type BLSSigner } from './supernet' +import { type BLSSigner, type SupernetAuth } from './supernet' import { AddDelegatorTransaction, AddPermissionlessDelegatorTransaction, @@ -196,10 +196,10 @@ export function buildAddValidatorTransaction ( shares: number, stakeAddresses: string[], stakeThreshold: number, - stakeTimelock: bigint, + stakeLocktime: bigint, rewardAddresses: string[], rewardThreshold: number, - rewardTimelock: bigint, + rewardLocktime: bigint, changeAddress: string, networkId: number, memo: string = '' @@ -226,11 +226,11 @@ export function buildAddValidatorTransaction ( const stake: TransferableOutput[] = [ new TransferableOutput( new AssetId(stakedAssetId), - new Secp256k1Output(stakeAmount, stakeTimelock, stakeThreshold, Address.toAddresses(stakeAddresses)) + new Secp256k1Output(stakeAmount, stakeLocktime, stakeThreshold, Address.toAddresses(stakeAddresses)) ) ] const rewardsOwner: Secp256k1OutputOwners = new Secp256k1OutputOwners( - rewardTimelock, + rewardLocktime, rewardThreshold, Address.toAddresses(rewardAddresses) ) @@ -269,10 +269,10 @@ export function buildAddDelegatorTransaction ( stakedAssetId: string, stakeAddresses: string[], stakeThreshold: number, - stakeTimelock: bigint, + stakeLocktime: bigint, rewardAddresses: string[], rewardThreshold: number, - rewardTimelock: bigint, + rewardLocktime: bigint, changeAddress: string, networkId: number, memo: string = '' @@ -299,11 +299,11 @@ export function buildAddDelegatorTransaction ( const stake: TransferableOutput[] = [ new TransferableOutput( new AssetId(stakedAssetId), - new Secp256k1Output(stakeAmount, stakeTimelock, stakeThreshold, Address.toAddresses(stakeAddresses)) + new Secp256k1Output(stakeAmount, stakeLocktime, stakeThreshold, Address.toAddresses(stakeAddresses)) ) ] const rewardsOwner: Secp256k1OutputOwners = new Secp256k1OutputOwners( - rewardTimelock, + rewardLocktime, rewardThreshold, Address.toAddresses(rewardAddresses) ) @@ -574,10 +574,10 @@ export function buildAddPermissionlessValidatorTransaction ( signer: BLSSigner, stakeAddresses: string[], stakeThreshold: number, - stakeTimelock: bigint, + stakeLocktime: bigint, rewardAddresses: string[], rewardThreshold: number, - rewardTimelock: bigint, + rewardLocktime: bigint, changeAddress: string, networkId: number, memo: string = '' @@ -604,11 +604,11 @@ export function buildAddPermissionlessValidatorTransaction ( const stake: TransferableOutput[] = [ new TransferableOutput( new AssetId(stakedAssetId), - new Secp256k1Output(stakeAmount, stakeTimelock, stakeThreshold, Address.toAddresses(stakeAddresses)) + new Secp256k1Output(stakeAmount, stakeLocktime, stakeThreshold, Address.toAddresses(stakeAddresses)) ) ] const rewardsOwner: Secp256k1OutputOwners = new Secp256k1OutputOwners( - rewardTimelock, + rewardLocktime, rewardThreshold, Address.toAddresses(rewardAddresses) ) @@ -647,10 +647,10 @@ export function buildAddPermissionlessDelegatorTransaction ( stakedAssetId: string, stakeAddresses: string[], stakeThreshold: number, - stakeTimelock: bigint, + stakeLocktime: bigint, rewardAddresses: string[], rewardThreshold: number, - rewardTimelock: bigint, + rewardLocktime: bigint, changeAddress: string, networkId: number, memo: string = '' @@ -677,11 +677,11 @@ export function buildAddPermissionlessDelegatorTransaction ( const stake: TransferableOutput[] = [ new TransferableOutput( new AssetId(stakedAssetId), - new Secp256k1Output(stakeAmount, stakeTimelock, stakeThreshold, Address.toAddresses(stakeAddresses)) + new Secp256k1Output(stakeAmount, stakeLocktime, stakeThreshold, Address.toAddresses(stakeAddresses)) ) ] const rewardsOwner: Secp256k1OutputOwners = new Secp256k1OutputOwners( - rewardTimelock, + rewardLocktime, rewardThreshold, Address.toAddresses(rewardAddresses) ) diff --git a/src/transaction/platform/status.ts b/src/transaction/platform/status.ts index 0077e8d3..cb10cc22 100644 --- a/src/transaction/platform/status.ts +++ b/src/transaction/platform/status.ts @@ -1,5 +1,5 @@ import { type PlatformAPI } from '../../api' -import { sleep } from '../../utils' +import { TimeUtils } from '../../utils' import { type TransactionStatusFetcher, TransactionStatusFetchDelay } from '../transaction' export enum PlatformTransactionStatus { @@ -25,7 +25,7 @@ export class PlatformTransactionStatusFetcher implements TransactionStatusFetche const maxAttempts: number = timeout / delay this.currentStatus = PlatformTransactionStatus.Processing while (this.attempts < maxAttempts && !this.isCurrentStatusSettled()) { - await sleep(delay) + await TimeUtils.sleep(delay) await this.platformApi.getTxStatus(this.transactionId).then( (value) => { this.currentStatus = value.status diff --git a/src/transaction/platform/transaction.ts b/src/transaction/platform/transaction.ts index 5e43330a..d4aff7dd 100644 --- a/src/transaction/platform/transaction.ts +++ b/src/transaction/platform/transaction.ts @@ -4,20 +4,20 @@ import { TransferableOutput } from '../output' import { type Signable } from '../signature' import { AbstractBaseTransaction, AbstractExportTransaction, AbstractImportTransaction } from '../transaction' import { - BlockchainIdSize, + type Address, + type AssetId, + AssetIdSize, BlockchainId, - type SupernetId, - SupernetIdSize, + BlockchainIdSize, type DynamicId, DynamicIdSize, - type AssetId, - type Address, - AssetIdSize, type NodeId, - NodeIdSize + NodeIdSize, + type SupernetId, + SupernetIdSize } from '../types' -import { SupernetAuth, type BLSSigner } from './supernet' -import { Validator, Secp256k1OutputOwners } from './validation' +import { type BLSSigner, SupernetAuth } from './supernet' +import { Secp256k1OutputOwners, Validator } from './validation' const AddValidatorTransactionTypeId: number = 0x0000000c const AddSupernetValidatorTransactionType: number = 0x0000000d diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 1d0052ee..a0b35648 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -14,7 +14,6 @@ const GENESIS_CODE: string = 'Genesis error' const ACCOUNT_CODE: string = 'Account error' const CHAIN_CODE: string = 'Chain error' const CROSS_CODE: string = 'Cross error' -const TIME_CODE: string = 'Time error' const STAKE_CODE: string = 'Stake error' const AMOUNT_CODE: string = 'Amount error' const CAPACITY_CODE: string = 'Capacity error' @@ -147,13 +146,6 @@ export class CrossError extends JuneoError { } } -export class TimeError extends JuneoError { - constructor (message: string) { - super(message, TIME_CODE) - Object.setPrototypeOf(this, TimeError.prototype) - } -} - export class StakeError extends JuneoError { constructor (message: string) { super(message, STAKE_CODE) diff --git a/src/utils/time.ts b/src/utils/time.ts index 02efe579..da36bfdb 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -1,23 +1,32 @@ -import { TimeError } from './errors' +export class TimeUtils { + private static INSTANCE: TimeUtils | undefined + private readonly provider: TimeProvider -// TODO get time from an external service to avoid using local machine timestamp -export function now (): bigint { - return BigInt(Math.round(new Date().getTime() / 1000)) -} + private constructor (provider: TimeProvider) { + this.provider = provider + } -export async function sleep (milliseconds: number): Promise { - await new Promise((resolve) => setTimeout(resolve, milliseconds)) -} + static getSingleton (): TimeUtils { + if (TimeUtils.INSTANCE === undefined) { + TimeUtils.INSTANCE = new TimeUtils(new TimeProvider()) + } + return TimeUtils.INSTANCE + } -export function verifyTimeRange (startTime: bigint, endTime: bigint, minPeriod: bigint): void { - if (startTime < now()) { - throw new TimeError('start time must be in the future') + // TODO get time from an external service to avoid using local machine timestamp + static now (): bigint { + return TimeUtils.getSingleton().provider.getCurrentClientTimeSeconds() } - if (endTime <= startTime) { - throw new TimeError('end time must be after start time') + + static async sleep (milliseconds: number): Promise { + await new Promise((resolve) => setTimeout(resolve, milliseconds)) } - const period: bigint = endTime - startTime - if (period < minPeriod) { - throw new TimeError(`period must be at least ${minPeriod} seconds`) +} + +class TimeProvider { + getCurrentClientTimeSeconds (): bigint { + return BigInt(Math.round(new Date().getTime() / 1000)) } + + // TODO add and use if available getCurrentServerTimeSeconds() } diff --git a/src/utils/utxo.ts b/src/utils/utxo.ts index a9edec31..d66445c7 100644 --- a/src/utils/utxo.ts +++ b/src/utils/utxo.ts @@ -95,7 +95,7 @@ export function calculateBalances (utxoSet: Utxo[], balances: Map { let success: boolean = false - const vmId: string = chain.vmId + const vmId: string = chain.vm.id if (vmId === JVM_ID) { success = await executable.trackJVMTransaction(transactionId, transactionType) } else if (vmId === PLATFORMVM_ID) { diff --git a/src/wallet/account/account.ts b/src/wallet/account/account.ts index d3eb0a35..3e6ea94c 100644 --- a/src/wallet/account/account.ts +++ b/src/wallet/account/account.ts @@ -3,10 +3,10 @@ import { type TokenAsset } from '../../asset' import { type Blockchain } from '../../chain' import { type MCNProvider } from '../../juneo' import { type Utxo } from '../../transaction' -import { type AssetValue, fetchUtxos, now, calculateBalances } from '../../utils' -import { type ChainOperationSummary, type ChainNetworkOperation } from '../operation' -import { type UtxoSpending, type Spending } from '../transaction' -import { type VMWallet, type MCNWallet } from '../wallet' +import { type AssetValue, calculateBalances, fetchUtxos, TimeUtils } from '../../utils' +import { type ChainNetworkOperation, type ChainOperationSummary } from '../operation' +import { type Spending, type UtxoSpending } from '../transaction' +import { type MCNWallet, type VMWallet } from '../wallet' import { Balance } from './balance' export enum AccountType { @@ -121,8 +121,8 @@ export abstract class AbstractChainAccount implements ChainAccount { export abstract class UtxoAccount extends AbstractChainAccount { utxoSet: Utxo[] = [] utxoSetMultiSig: Utxo[] = [] - utxoSetTimelocked: Utxo[] = [] - readonly timelockedBalances: Map = new Map() + utxoSetLocked: Utxo[] = [] + readonly lockedBalances: Map = new Map() protected fetching: boolean = false private readonly utxoApi: AbstractUtxoAPI @@ -131,24 +131,24 @@ export abstract class UtxoAccount extends AbstractChainAccount { this.utxoApi = utxoApi } - getTimelockedAssetValue (asset: TokenAsset): AssetValue { - return asset.getAssetValue(this.getTimelockedAmount(asset.assetId)) + getLockedAssetValue (asset: TokenAsset): AssetValue { + return asset.getAssetValue(this.getLockedAmount(asset.assetId)) } - async getTimelockedValue (provider: MCNProvider, assetId: string): Promise { + async getLockedValue (provider: MCNProvider, assetId: string): Promise { const asset: TokenAsset = await this.chain.getAsset(provider, assetId) - return this.getTimelockedAssetValue(asset) + return this.getLockedAssetValue(asset) } - getTimelockedAmount (assetId: string): bigint { - return this.getTimelockedBalance(assetId).getValue() + getLockedAmount (assetId: string): bigint { + return this.getLockedBalance(assetId).getValue() } - getTimelockedBalance (assetId: string): Balance { - if (!this.timelockedBalances.has(assetId)) { - this.timelockedBalances.set(assetId, new Balance()) + getLockedBalance (assetId: string): Balance { + if (!this.lockedBalances.has(assetId)) { + this.lockedBalances.set(assetId, new Balance()) } - return this.timelockedBalances.get(assetId)! + return this.lockedBalances.get(assetId)! } async fetchBalance (assetId: string): Promise { @@ -168,7 +168,7 @@ export abstract class UtxoAccount extends AbstractChainAccount { this.utxoSet = await fetchUtxos(this.utxoApi, [this.address]) this.sortUtxoSet() calculateBalances(this.utxoSet, this.balances) - calculateBalances(this.utxoSetTimelocked, this.timelockedBalances) + calculateBalances(this.utxoSetLocked, this.lockedBalances) this.fetching = false } @@ -204,11 +204,11 @@ export abstract class UtxoAccount extends AbstractChainAccount { private sortUtxoSet (): void { const spendableUtxoSet: Utxo[] = [] this.utxoSetMultiSig = [] - this.utxoSetTimelocked = [] - const currentTime: bigint = now() + this.utxoSetLocked = [] + const currentTime: bigint = TimeUtils.now() for (const utxo of this.utxoSet) { if (utxo.output.locktime > currentTime) { - this.utxoSetTimelocked.push(utxo) + this.utxoSetLocked.push(utxo) } else if (!this.hasThreshold(utxo)) { this.utxoSetMultiSig.push(utxo) } else { diff --git a/src/wallet/account/balance.ts b/src/wallet/account/balance.ts index 9bedad80..75670e3c 100644 --- a/src/wallet/account/balance.ts +++ b/src/wallet/account/balance.ts @@ -1,4 +1,4 @@ -import { now } from '../../utils' +import { TimeUtils } from '../../utils' export enum BalanceStatus { Updating = 'Updating', @@ -56,7 +56,7 @@ export class Balance { } shouldUpdate (): boolean { - return now() < this.lastUpdate + UpdateTimeValidity + return TimeUtils.now() < this.lastUpdate + UpdateTimeValidity } getStatus (): BalanceStatus { @@ -75,7 +75,7 @@ export class Balance { } private callBalanceUpdateEvent (event: BalanceUpdateEvent): void { - this.lastUpdate = now() + this.lastUpdate = TimeUtils.now() this.listeners.forEach((listener) => { listener.onBalanceUpdateEvent(event) }) diff --git a/src/wallet/account/evm.ts b/src/wallet/account/evm.ts index 4bc87680..05ca4ed6 100644 --- a/src/wallet/account/evm.ts +++ b/src/wallet/account/evm.ts @@ -1,30 +1,23 @@ +import { ethers } from 'ethers' import { type JEVMBlockchain } from '../../chain' import { type MCNProvider } from '../../juneo' -import { AccountError } from '../../utils' +import { AccountError, isContractAddress } from '../../utils' import { - type CancelStreamOperation, type ChainNetworkOperation, - ChainOperationSummary, + type ChainOperationSummary, type EthCallOperation, type ExecutableOperation, NetworkOperationType, - type RedeemAuctionOperation, type SendOperation, type UnwrapOperation, - type WithdrawStreamOperation, type WrapOperation } from '../operation' import { BaseSpending, type EVMFeeData, + FeeType, TransactionType, - estimateEVMCancelStreamOperation, - estimateEVMRedeemAuctionOperation, - estimateEVMTransfer, - estimateEVMUnwrapOperation, - estimateEVMWithdrawStreamOperation, - estimateEVMWrapOperation, - estimateEthCallOperation, + estimateEVMOperation, executeEVMTransaction } from '../transaction' import { type JEVMWallet, type MCNWallet } from '../wallet' @@ -47,41 +40,72 @@ export class EVMAccount extends AbstractChainAccount { const provider: MCNProvider = await this.provider.getStaticProvider() if (operation.type === NetworkOperationType.Send) { const send: SendOperation = operation as SendOperation - const fee: EVMFeeData = await estimateEVMTransfer( + const isContract: boolean = isContractAddress(send.assetId) + const address: string = isContract ? send.assetId : send.address + const amount: bigint = isContract ? BigInt(0) : send.amount + const data: string = isContract + ? await this.chain.getContractTransactionData(provider, send.assetId, address, amount) + : '0x' + return await estimateEVMOperation( provider, - this.chainWallet, - this.chain.id, - send.assetId, - send.amount, - send.address + this.chain, + this.chainWallet.getAddress(), + send, + [new BaseSpending(this.chain, send.amount, send.assetId)], + new Map([[send.assetId, send.amount]]), + address, + amount, + data, + FeeType.BaseFee ) - const spending: BaseSpending = new BaseSpending(this.chain, send.amount, send.assetId) - const values = new Map([[send.assetId, send.amount]]) - return new ChainOperationSummary(provider, operation, this.chain, fee, [spending, fee.spending], values) } else if (operation.type === NetworkOperationType.Wrap) { - return await estimateEVMWrapOperation(provider, this.chainWallet.getAddress(), operation as WrapOperation) - } else if (operation.type === NetworkOperationType.Unwrap) { - return await estimateEVMUnwrapOperation(provider, this.chainWallet.getAddress(), operation as UnwrapOperation) - } else if (operation.type === NetworkOperationType.RedeemAuction) { - return await estimateEVMRedeemAuctionOperation( + const wrap = operation as WrapOperation + return await estimateEVMOperation( provider, + this.chain, this.chainWallet.getAddress(), - operation as RedeemAuctionOperation + wrap, + [new BaseSpending(this.chain, wrap.amount, this.chain.assetId)], + new Map([[this.chain.assetId, wrap.amount]]), + wrap.asset.address, + wrap.amount, + wrap.asset.adapter.getDepositData(), + FeeType.Wrap ) - } else if (operation.type === NetworkOperationType.WithdrawStream) { - return await estimateEVMWithdrawStreamOperation( + } else if (operation.type === NetworkOperationType.Unwrap) { + const unwrap = operation as UnwrapOperation + return await estimateEVMOperation( provider, + this.chain, this.chainWallet.getAddress(), - operation as WithdrawStreamOperation + unwrap, + [new BaseSpending(this.chain, unwrap.amount, unwrap.asset.assetId)], + new Map([[unwrap.asset.assetId, unwrap.amount]]), + unwrap.asset.address, + BigInt(0), + unwrap.asset.adapter.getWithdrawData(unwrap.amount), + FeeType.Unwrap ) - } else if (operation.type === NetworkOperationType.CancelStream) { - return await estimateEVMCancelStreamOperation( + } else if (operation.type === NetworkOperationType.EthCall) { + const ethCall = operation as EthCallOperation + const contract = new ethers.Contract(ethCall.contract, ethCall.abi, this.chain.ethProvider) + const data = contract.interface.encodeFunctionData(ethCall.functionName, ethCall.values) + const values = new Map() + if (ethCall.amount > 0) { + values.set(this.chain.assetId, ethCall.amount) + } + return await estimateEVMOperation( provider, + this.chain, this.chainWallet.getAddress(), - operation as CancelStreamOperation + ethCall, + [], + values, + ethCall.contract, + ethCall.amount, + data, + FeeType.EthCall ) - } else if (operation.type === NetworkOperationType.EthCall) { - return await estimateEthCallOperation(provider, this.chainWallet.getAddress(), operation as EthCallOperation) } throw new AccountError(`unsupported operation: ${operation.type} for the chain with id: ${this.chain.id}`) } @@ -95,12 +119,6 @@ export class EVMAccount extends AbstractChainAccount { await this.executeAndTrackTransaction(summary, TransactionType.Wrap) } else if (operation === NetworkOperationType.Unwrap) { await this.executeAndTrackTransaction(summary, TransactionType.Unwrap) - } else if (operation === NetworkOperationType.RedeemAuction) { - await this.executeAndTrackTransaction(summary, TransactionType.RedeemAuction) - } else if (operation === NetworkOperationType.WithdrawStream) { - await this.executeAndTrackTransaction(summary, TransactionType.WithdrawStream) - } else if (operation === NetworkOperationType.CancelStream) { - await this.executeAndTrackTransaction(summary, TransactionType.CancelStream) } else if (operation === NetworkOperationType.EthCall) { await this.executeAndTrackTransaction(summary, TransactionType.EthCall) } diff --git a/src/wallet/account/mcn.ts b/src/wallet/account/mcn.ts index 94a81bac..0260b5f2 100644 --- a/src/wallet/account/mcn.ts +++ b/src/wallet/account/mcn.ts @@ -1,29 +1,29 @@ -import { AccountError, type AssetValue, sortSpendings } from '../../utils' -import { type MCNWallet } from '../wallet' +import { type TokenAsset } from '../../asset' +import { type Blockchain } from '../../chain' +import { type MCNProvider } from '../../juneo' +import { AccountError, sortSpendings, type AssetValue } from '../../utils' import { - NetworkOperationType, - NetworkOperationStatus, - type NetworkOperation, - type CrossOperationSummary, - type ExecutableOperation, - type ChainOperationSummary, - type OperationSummary, - type CrossResumeOperationSummary, NetworkOperationRange, + NetworkOperationStatus, + NetworkOperationType, type ChainNetworkOperation, - type CrossResumeOperation, + type ChainOperationSummary, type CrossOperation, + type CrossOperationSummary, + type CrossResumeOperation, + type CrossResumeOperationSummary, type DepositResumeOperation, - type DepositResumeOperationSummary + type DepositResumeOperationSummary, + type ExecutableOperation, + type NetworkOperation, + type OperationSummary } from '../operation' -import { AccountType, type UtxoAccount, type ChainAccount, type AbstractChainAccount } from './account' +import { CrossManager, type Spending } from '../transaction' +import { type MCNWallet } from '../wallet' +import { AccountType, type AbstractChainAccount, type ChainAccount, type UtxoAccount } from './account' import { EVMAccount } from './evm' import { JVMAccount } from './jvm' import { PlatformAccount } from './platform' -import { type Spending, CrossManager } from '../transaction' -import { type Blockchain } from '../../chain' -import { type TokenAsset } from '../../asset' -import { type MCNProvider } from '../../juneo' export class MCNAccount { readonly wallet: MCNWallet @@ -67,15 +67,15 @@ export class MCNAccount { return totalAmount } - getTotalTimelockedAssetValue (asset: TokenAsset): AssetValue { - return asset.getAssetValue(this.getTotalTimelockedAmount(asset.assetId)) + getTotalLockedAssetValue (asset: TokenAsset): AssetValue { + return asset.getAssetValue(this.getTotalLockedAmount(asset.assetId)) } - getTotalTimelockedAmount (assetId: string): bigint { + getTotalLockedAmount (assetId: string): bigint { let totalAmount: bigint = BigInt(0) for (const [, account] of this.chainAccounts) { if (account.type === AccountType.Utxo) { - totalAmount += (account as UtxoAccount).getTimelockedBalance(assetId).getValue() + totalAmount += (account as UtxoAccount).getLockedBalance(assetId).getValue() } } return totalAmount diff --git a/src/wallet/operation/operation.ts b/src/wallet/operation/operation.ts index 4d19e853..de11a8f7 100644 --- a/src/wallet/operation/operation.ts +++ b/src/wallet/operation/operation.ts @@ -13,9 +13,6 @@ export enum NetworkOperationType { DelegatePrimary = 'Delegate primary', Wrap = 'Wrap', Unwrap = 'Unwrap', - RedeemAuction = 'Redeem auction', - WithdrawStream = 'Withdraw stream', - CancelStream = 'Cancel stream', CreateSupernet = 'Create supernet', ValidateSupernet = 'Validate supernet', RemoveSupernetValidator = 'Remove supernet validator', @@ -122,43 +119,6 @@ export class UnwrapOperation extends Wrapping { } } -export class RedeemAuctionOperation extends JEVMChainOperation { - auctionAddress: string - auctionId: bigint - - constructor (chain: JEVMBlockchain, auctionAddress: string, auctionId: bigint) { - super(NetworkOperationType.RedeemAuction, chain) - this.auctionAddress = auctionAddress - this.auctionId = auctionId - } -} - -abstract class StreamOperation extends JEVMChainOperation { - streamAddress: string - streamId: bigint - - constructor (type: NetworkOperationType, chain: JEVMBlockchain, streamAddress: string, streamId: bigint) { - super(type, chain) - this.streamAddress = streamAddress - this.streamId = streamId - } -} - -export class WithdrawStreamOperation extends StreamOperation { - amount: bigint - - constructor (chain: JEVMBlockchain, streamAddress: string, streamId: bigint, amount: bigint) { - super(NetworkOperationType.WithdrawStream, chain, streamAddress, streamId) - this.amount = amount - } -} - -export class CancelStreamOperation extends StreamOperation { - constructor (chain: JEVMBlockchain, streamAddress: string, streamId: bigint) { - super(NetworkOperationType.CancelStream, chain, streamAddress, streamId) - } -} - export class EthCallOperation extends JEVMChainOperation { contract: string abi: string diff --git a/src/wallet/operation/summary.ts b/src/wallet/operation/summary.ts index 5475ec2f..61d500d8 100644 --- a/src/wallet/operation/summary.ts +++ b/src/wallet/operation/summary.ts @@ -1,15 +1,15 @@ -import { type PlatformBlockchain, type Blockchain } from '../../chain' +import { type Blockchain, type PlatformBlockchain } from '../../chain' import { type MCNProvider } from '../../juneo' import { type Utxo } from '../../transaction' -import { type UtxoFeeData, type FeeData, type Spending, type EVMFeeData } from '../transaction' +import { type EVMFeeData, type FeeData, type Spending, type UtxoFeeData } from '../transaction' import { ExecutableOperation } from './executable' import { - type Staking, - type NetworkOperation, - type CrossResumeOperation, type ChainNetworkOperation, type CrossOperation, - type DepositResumeOperation + type CrossResumeOperation, + type DepositResumeOperation, + type NetworkOperation, + type Staking } from './operation' export interface OperationSummary { @@ -23,6 +23,8 @@ export interface OperationSummary { getAssets: () => Set getChains: () => Blockchain[] + + getErrors: () => Error[] } abstract class AbstractOperationSummary implements OperationSummary { @@ -30,6 +32,7 @@ abstract class AbstractOperationSummary implements OperationSummary { fees: FeeData[] spendings: Spending[] values: Map + errors: Error[] private readonly executable: ExecutableOperation constructor ( @@ -37,13 +40,15 @@ abstract class AbstractOperationSummary implements OperationSummary { operation: NetworkOperation, fees: FeeData[], spendings: Spending[], - values: Map + values: Map, + errors: Error[] ) { this.operation = operation this.fees = fees this.spendings = spendings this.values = values this.executable = new ExecutableOperation(provider) + this.errors = errors } getExecutable (): ExecutableOperation { @@ -69,6 +74,10 @@ abstract class AbstractOperationSummary implements OperationSummary { } abstract getChains (): Blockchain[] + + getErrors (): Error[] { + return this.errors + } } export class ChainOperationSummary extends AbstractOperationSummary { @@ -82,9 +91,10 @@ export class ChainOperationSummary extends AbstractOperationSummary { chain: Blockchain, fee: FeeData, spendings: Spending[], - values: Map + values: Map, + errors: Error[] = [] ) { - super(provider, operation, [fee], spendings, values) + super(provider, operation, [fee], spendings, values, errors) this.operation = operation this.chain = chain this.fee = fee @@ -105,9 +115,10 @@ export class CrossOperationSummary extends AbstractOperationSummary { chains: Blockchain[], fees: FeeData[], spendings: Spending[], - values: Map + values: Map, + errors: Error[] = [] ) { - super(provider, operation, fees, spendings, values) + super(provider, operation, fees, spendings, values, errors) this.operation = operation this.chains = chains } @@ -127,9 +138,10 @@ export class StakingOperationSummary extends ChainOperationSummary { fee: UtxoFeeData, spendings: Spending[], values: Map, - potentialReward: bigint + potentialReward: bigint, + errors: Error[] = [] ) { - super(provider, operation, chain, fee, spendings, values) + super(provider, operation, chain, fee, spendings, values, errors) this.potentialReward = potentialReward } } @@ -147,9 +159,10 @@ export class CrossResumeOperationSummary extends ChainOperationSummary { spendings: Spending[], values: Map, payImportFee: boolean, - utxoSet: Utxo[] + utxoSet: Utxo[], + errors: Error[] = [] ) { - super(provider, operation, operation.destination, importFee, spendings, values) + super(provider, operation, operation.destination, importFee, spendings, values, errors) this.operation = operation this.importFee = importFee this.payImportFee = payImportFee @@ -166,9 +179,10 @@ export class DepositResumeOperationSummary extends ChainOperationSummary { operation: DepositResumeOperation, fee: EVMFeeData, spendings: Spending[], - values: Map + values: Map, + errors: Error[] = [] ) { - super(provider, operation, operation.chain, fee, spendings, values) + super(provider, operation, operation.chain, fee, spendings, values, errors) this.operation = operation this.fee = fee } diff --git a/src/wallet/transaction/constants.ts b/src/wallet/transaction/constants.ts index ecc3a439..50e8de99 100644 --- a/src/wallet/transaction/constants.ts +++ b/src/wallet/transaction/constants.ts @@ -6,12 +6,5 @@ export const WalletStatusFetcherDelay: number = 100 export const MaxInvalidNonceAttempts: number = 5 export const InvalidNonceRetryDelay: number = 1000 -export const DefaultWrapEstimate: bigint = BigInt(55_000) -export const DefaultUnwrapEstimate: bigint = BigInt(45_000) -export const DefaultTransferEstimate: bigint = BigInt(200_000) export const DefaultWithdrawEstimate: bigint = BigInt(100_000) export const DefaultDepositEstimate: bigint = BigInt(100_000) -export const DefaultRedeemAuctionEstimate: bigint = BigInt(100_000) -export const DefaultWithdrawStreamEstimate: bigint = BigInt(100_000) -export const DefaultCancelStreamEstimate: bigint = BigInt(125_000) -export const DefaultEthCallEstimate: bigint = BigInt(1_000_000) diff --git a/src/wallet/transaction/cross/cross.ts b/src/wallet/transaction/cross/cross.ts index 07a13bea..bd254d0a 100644 --- a/src/wallet/transaction/cross/cross.ts +++ b/src/wallet/transaction/cross/cross.ts @@ -1,9 +1,33 @@ +import { + BaseFeeData, + BaseSpending, + estimateEVMDepositJRC20, + estimateEVMExportTransaction, + estimateEVMImportTransaction, + estimateEVMWithdrawJRC20, + estimateJVMExportTransaction, + estimateJVMImportTransaction, + estimatePlatformExportTransaction, + estimatePlatformImportTransaction, + executeEVMExportTransaction, + executeEVMImportTransaction, + executeEVMTransaction, + executeJVMExportTransaction, + executeJVMImportTransaction, + executePlatformExportTransaction, + executePlatformImportTransaction, + FeeType, + TransactionType, + type EVMFeeData, + type FeeData, + type Spending +} from '..' import { type AbstractUtxoAPI, type JEVMAPI } from '../../../api' import { type JRC20Asset } from '../../../asset' import { + JEVM_ID, JVM_ID, PLATFORMVM_ID, - JEVM_ID, type Blockchain, type JEVMBlockchain, type JVMBlockchain @@ -13,46 +37,22 @@ import { type Secp256k1Output, type Utxo } from '../../../transaction' import { AtomicDenomination, CrossError, + fetchUtxos, getUtxoAPI, getUtxosAmountValues, - trackJuneoTransaction, - fetchUtxos, - sleep + TimeUtils, + trackJuneoTransaction } from '../../../utils' -import { type EVMAccount, type ChainAccount, type MCNAccount, type UtxoAccount } from '../../account' +import { type ChainAccount, type EVMAccount, type MCNAccount, type UtxoAccount } from '../../account' import { - type ExecutableOperation, + CrossOperation, CrossOperationSummary, - CrossResumeOperationSummary, CrossResumeOperation, - CrossOperation, + CrossResumeOperationSummary, DepositResumeOperation, - DepositResumeOperationSummary + DepositResumeOperationSummary, + type ExecutableOperation } from '../../operation' -import { - estimateEVMExportTransaction, - estimateEVMImportTransaction, - estimateJVMExportTransaction, - estimateJVMImportTransaction, - estimatePlatformExportTransaction, - estimatePlatformImportTransaction, - executeJVMExportTransaction, - type FeeData, - executePlatformExportTransaction, - executeJVMImportTransaction, - executePlatformImportTransaction, - executeEVMImportTransaction, - executeEVMExportTransaction, - BaseFeeData, - TransactionType, - type Spending, - BaseSpending, - FeeType, - type EVMFeeData, - estimateEVMWithdrawJRC20, - executeEVMTransaction, - estimateEVMDepositJRC20 -} from '..' import { type MCNWallet } from '../../wallet' export class CrossManager { @@ -71,11 +71,11 @@ export class CrossManager { utxosCount?: number, assetsCount?: number ): Promise { - if (destination.vmId === JVM_ID) { + if (destination.vm.id === JVM_ID) { return await estimateJVMImportTransaction(provider) - } else if (destination.vmId === PLATFORMVM_ID) { + } else if (destination.vm.id === PLATFORMVM_ID) { return await estimatePlatformImportTransaction(provider) - } else if (destination.vmId === JEVM_ID) { + } else if (destination.vm.id === JEVM_ID) { const api: JEVMAPI = provider.jevmApi[destination.id] const exportedAssetsCount: number = assetId === api.chain.assetId ? 1 : 2 const inputsCount: number = typeof utxosCount === 'number' ? utxosCount : exportedAssetsCount @@ -83,7 +83,7 @@ export class CrossManager { const outputsCount: number = typeof assetsCount === 'number' ? assetsCount : exportedAssetsCount // 1 return await estimateEVMImportTransaction(api, inputsCount, outputsCount) } - throw new CrossError(`destination vm id does not support cross: ${destination.vmId}`) + throw new CrossError(`destination vm id does not support cross: ${destination.vm.id}`) } async estimateExport ( @@ -92,19 +92,19 @@ export class CrossManager { destination: Blockchain, assetId: string ): Promise { - if (source.vmId === JVM_ID) { + if (source.vm.id === JVM_ID) { return await estimateJVMExportTransaction(provider) - } else if (source.vmId === PLATFORMVM_ID) { + } else if (source.vm.id === PLATFORMVM_ID) { return await estimatePlatformExportTransaction(provider) - } else if (source.vmId === JEVM_ID) { + } else if (source.vm.id === JEVM_ID) { const api: JEVMAPI = provider.jevmApi[source.id] return await estimateEVMExportTransaction(api, assetId, destination) } - throw new CrossError(`source vm id does not support cross: ${source.vmId}`) + throw new CrossError(`source vm id does not support cross: ${source.vm.id}`) } canPayImportFee (destination: Blockchain, importFee: bigint, importFeeAssetDestinationBalance: bigint): boolean { - return destination.vmId !== JEVM_ID ? importFeeAssetDestinationBalance >= importFee : false + return destination.vm.id !== JEVM_ID ? importFeeAssetDestinationBalance >= importFee : false } shouldSendImportFee ( @@ -131,8 +131,8 @@ export class CrossManager { // initial cross transaction can be finalized by doing a regular JVM to JUNE chain cross transaction with the // initial exported funds towards it. return ( - cross.source.vmId === JEVM_ID && - cross.destination.vmId === JEVM_ID && + cross.source.vm.id === JEVM_ID && + cross.destination.vm.id === JEVM_ID && cross.source.id !== this.provider.juneChain.id ) } @@ -153,7 +153,7 @@ export class CrossManager { if (source.id === destination.id) { throw new CrossError('source and destination chain cannot be the same') } - if (source.vmId === JVM_ID) { + if (source.vm.id === JVM_ID) { return await executeJVMExportTransaction( provider, this.wallet, @@ -167,7 +167,7 @@ export class CrossManager { utxoSet, extraFeeAmount ) - } else if (source.vmId === PLATFORMVM_ID) { + } else if (source.vm.id === PLATFORMVM_ID) { return await executePlatformExportTransaction( provider, this.wallet, @@ -180,7 +180,7 @@ export class CrossManager { exportFee, utxoSet ) - } else if (source.vmId === JEVM_ID) { + } else if (source.vm.id === JEVM_ID) { const api: JEVMAPI = provider.jevmApi[source.id] return await executeEVMExportTransaction( provider, @@ -195,7 +195,7 @@ export class CrossManager { exportFee ) } - throw new CrossError(`source vm id does not support cross: ${source.vmId}`) + throw new CrossError(`source vm id does not support cross: ${source.vm.id}`) } async import ( @@ -209,18 +209,18 @@ export class CrossManager { if (source.id === destination.id) { throw new CrossError('source and destination chain cannot be the same') } - if (destination.vmId === JVM_ID) { + if (destination.vm.id === JVM_ID) { return await executeJVMImportTransaction(provider, this.wallet, source, importFee, utxoSet) - } else if (destination.vmId === PLATFORMVM_ID) { + } else if (destination.vm.id === PLATFORMVM_ID) { return await executePlatformImportTransaction(provider, this.wallet, source, importFee, utxoSet) - } else if (destination.vmId === JEVM_ID) { + } else if (destination.vm.id === JEVM_ID) { if (payImportFee) { - throw new CrossError(`vm id ${destination.vmId} cannot pay import fee`) + throw new CrossError(`vm id ${destination.vm.id} cannot pay import fee`) } const api: JEVMAPI = provider.jevmApi[destination.id] return await executeEVMImportTransaction(provider, api, this.wallet, source, importFee, utxoSet) } - throw new CrossError(`destination vm id does not support cross: ${destination.vmId}`) + throw new CrossError(`destination vm id does not support cross: ${destination.vm.id}`) } async estimateCrossOperation (cross: CrossOperation, account: MCNAccount): Promise { @@ -353,7 +353,7 @@ export class CrossManager { cross.destination = jvmChain await this.executeCrossOperationStep(summary, account, cross, summary.fees[0], summary.fees[1]) // jevm cross transactions amount must be changed because of atomic denominator - if (cross.source.vmId === JEVM_ID && cross.assetId === cross.source.assetId) { + if (cross.source.vm.id === JEVM_ID && cross.assetId === cross.source.assetId) { cross.amount /= AtomicDenomination } cross.source = jvmChain @@ -398,8 +398,9 @@ export class CrossManager { const status: string = executable.receipts[executable.receipts.length - 1].transactionStatus throw new CrossError(`error during withdraw transaction ${transactionHash} status fetching: ${status}`) } - // generate withdraw utxos - await sleep(500) + // wait until withdraw utxos are generated (should be max block time) + // it may be better to do checks of existence of such utxos instead of assuming they exist after waiting + await TimeUtils.sleep(2000) // feeData.data.to is the jrc20 address for withdraw transactions // cross.assetId should be jrc20.nativeAssetId here await juneAccount.fetchBalances([cross.assetId, feeData.data.to, feeData.assetId]) @@ -407,7 +408,7 @@ export class CrossManager { const balancesSync: Array> = [] let sourceUtxos: Utxo[] = [] const sourceAccount: ChainAccount = account.getAccount(cross.source.id) - if (cross.source.vmId === JVM_ID || cross.source.vmId === PLATFORMVM_ID) { + if (cross.source.vm.id === JVM_ID || cross.source.vm.id === PLATFORMVM_ID) { sourceUtxos = (sourceAccount as UtxoAccount).utxoSet } const destinationAccount: ChainAccount = account.getAccount(cross.destination.id) @@ -450,7 +451,7 @@ export class CrossManager { cross.source.id, exportTransactionId ) - const destinationVmId: string = cross.destination.vmId + const destinationVmId: string = cross.destination.vm.id if (!cross.sendImportFee && (destinationVmId === JVM_ID || destinationVmId === PLATFORMVM_ID)) { destinationUtxos.push(...(destinationAccount as UtxoAccount).utxoSet) } diff --git a/src/wallet/transaction/cross/evm.ts b/src/wallet/transaction/cross/evm.ts index 46d76725..b801d3e3 100644 --- a/src/wallet/transaction/cross/evm.ts +++ b/src/wallet/transaction/cross/evm.ts @@ -3,24 +3,24 @@ import { type Blockchain } from '../../../chain' import { type MCNProvider } from '../../../juneo' import { type JEVMExportTransaction, - buildJEVMExportTransaction, + type JEVMImportTransaction, UserInput, type Utxo, - type JEVMImportTransaction, + buildJEVMExportTransaction, buildJEVMImportTransaction } from '../../../transaction' import { - estimateAtomicExportGas, - calculateAtomicCost, AtomicDenomination, - sleep, + TimeUtils, TransactionError, + calculateAtomicCost, + estimateAtomicExportGas, estimateAtomicImportGas, - getUtxosAmountValues, - getImportUserInputs + getImportUserInputs, + getUtxosAmountValues } from '../../../utils' -import { type MCNWallet, type JEVMWallet, type VMWallet } from '../../wallet' -import { MaxInvalidNonceAttempts, InvalidNonceRetryDelay } from '../constants' +import { type JEVMWallet, type MCNWallet, type VMWallet } from '../../wallet' +import { InvalidNonceRetryDelay, MaxInvalidNonceAttempts } from '../constants' import { estimateEVMBaseFee, getWalletNonce } from '../evm' import { BaseFeeData, type FeeData, FeeType } from '../fee' @@ -85,7 +85,7 @@ export async function executeEVMExportTransaction ( if (typeof transactionId === 'string') { return transactionId } - await sleep(InvalidNonceRetryDelay) + await TimeUtils.sleep(InvalidNonceRetryDelay) nonce = await getWalletNonce(evmWallet, api, true) } throw new TransactionError(`could not provide a valid nonce ${evmWallet.nonce}`) diff --git a/src/wallet/transaction/evm.ts b/src/wallet/transaction/evm.ts index e4e5a221..263848c2 100644 --- a/src/wallet/transaction/evm.ts +++ b/src/wallet/transaction/evm.ts @@ -1,73 +1,19 @@ -import { ethers } from 'ethers' +import { type ethers } from 'ethers' import { type JEVMAPI } from '../../api' import { type JRC20Asset } from '../../asset' -import { - AuctionContractAdapter, - type JEVMBlockchain, - NativeAssetCallContract, - SendEtherGasLimit, - StreamContractAdapter -} from '../../chain' +import { type JEVMBlockchain, NativeAssetCallContract } from '../../chain' import { type MCNProvider } from '../../juneo' -import { TransactionError, isContractAddress, sleep } from '../../utils' -import { - type CancelStreamOperation, - ChainOperationSummary, - type EthCallOperation, - type RedeemAuctionOperation, - type UnwrapOperation, - type WithdrawStreamOperation, - type WrapOperation -} from '../operation' +import { TimeUtils, TransactionError } from '../../utils' +import { type ChainNetworkOperation, ChainOperationSummary } from '../operation' import { type JEVMWallet } from '../wallet' import { - DefaultCancelStreamEstimate, DefaultDepositEstimate, - DefaultRedeemAuctionEstimate, - DefaultTransferEstimate, - DefaultUnwrapEstimate, DefaultWithdrawEstimate, - DefaultWithdrawStreamEstimate, - DefaultWrapEstimate, InvalidNonceRetryDelay, MaxInvalidNonceAttempts } from './constants' -import { BaseFeeData, FeeType } from './fee' -import { BaseSpending } from './transaction' - -export class EVMTransactionData { - from: string - to: string - value: bigint - data: string - - constructor (from: string, to: string, value: bigint, data: string) { - this.from = from - this.to = to - this.value = value - this.data = data - } -} - -export class EVMFeeData extends BaseFeeData { - baseFee: bigint - gasLimit: bigint - data: EVMTransactionData - - constructor ( - chain: JEVMBlockchain, - amount: bigint, - type: string, - baseFee: bigint, - gasLimit: bigint, - data: EVMTransactionData - ) { - super(chain, amount, type) - this.baseFee = baseFee - this.gasLimit = gasLimit - this.data = data - } -} +import { EVMFeeData, FeeType } from './fee' +import { type BaseSpending, EVMTransactionData } from './transaction' export async function getWalletNonce (wallet: JEVMWallet, api: JEVMAPI, synchronize: boolean): Promise { if (synchronize || !wallet.synchronized) { @@ -92,10 +38,8 @@ export async function estimateEVMCall ( to: string, value: bigint, data: string, - type: FeeType, - baseFeeAmount?: bigint -): Promise { - const baseFee: bigint = typeof baseFeeAmount === 'undefined' ? await estimateEVMBaseFee(api) : baseFeeAmount + baseFee: bigint +): Promise { const gasLimit: bigint = await api.chain.ethProvider.estimateGas({ blockTag: 'pending', from, @@ -108,38 +52,41 @@ export async function estimateEVMCall ( gasPrice: baseFee, data }) - const transactionData: EVMTransactionData = new EVMTransactionData(from, to, value, data) - return new EVMFeeData(api.chain, baseFee * gasLimit, type, baseFee, gasLimit, transactionData) + return gasLimit } -export async function estimateEVMTransfer ( +export async function estimateEVMOperation ( provider: MCNProvider, - wallet: JEVMWallet, - chainId: string, - assetId: string, + chain: JEVMBlockchain, + from: string, + operation: ChainNetworkOperation, + spendings: BaseSpending[], + values: Map, + address: string, amount: bigint, - address: string -): Promise { - const api: JEVMAPI = provider.jevmApi[chainId] - const isContract: boolean = isContractAddress(assetId) - const from: string = wallet.getAddress() - const to: string = isContract ? assetId : address - const value: bigint = isContract ? BigInt(0) : amount - const data: string = isContract - ? await api.chain.getContractTransactionData(provider, assetId, address, amount) - : '0x' - const type: FeeType = FeeType.BaseFee + data: string, + feeType: FeeType +): Promise { + const api: JEVMAPI = provider.jevmApi[chain.id] const baseFee: bigint = await estimateEVMBaseFee(api) - if (assetId === api.chain.assetId) { - const gasLimit: bigint = SendEtherGasLimit - const transactionData: EVMTransactionData = new EVMTransactionData(from, to, value, data) - return new EVMFeeData(api.chain, baseFee * gasLimit, type, baseFee, gasLimit, transactionData) - } - return await estimateEVMCall(api, from, to, value, data, type, baseFee).catch(() => { - const gasLimit: bigint = DefaultTransferEstimate - const transactionData: EVMTransactionData = new EVMTransactionData(from, to, value, data) - return new EVMFeeData(api.chain, baseFee * gasLimit, type, baseFee, gasLimit, transactionData) - }) + const fee = new EVMFeeData( + chain, + baseFee * BigInt(0), + feeType, + baseFee, + BigInt(0), + new EVMTransactionData(from, address, amount, data) + ) + spendings.push(fee.spending) + return await estimateEVMCall(api, from, address, amount, data, baseFee).then( + (gasLimit) => { + fee.setGasLimit(gasLimit) + return new ChainOperationSummary(provider, operation, chain, fee, spendings, values, []) + }, + (error) => { + return new ChainOperationSummary(provider, operation, chain, fee, spendings, values, [error]) + } + ) } export async function executeEVMTransaction ( @@ -172,216 +119,35 @@ export async function executeEVMTransaction ( if (typeof transactionId === 'string') { return transactionId } - await sleep(InvalidNonceRetryDelay) + await TimeUtils.sleep(InvalidNonceRetryDelay) unsignedTransaction.nonce = Number(await getWalletNonce(wallet, api, true)) } throw new TransactionError(`could not provide a valid nonce: ${wallet.nonce}`) } -export async function estimateEVMWrapOperation ( - provider: MCNProvider, - from: string, - wrap: WrapOperation -): Promise { - const chain: JEVMBlockchain = wrap.chain - const api: JEVMAPI = provider.jevmApi[chain.id] - const data: string = wrap.asset.adapter.getDepositData() - const type: FeeType = FeeType.Wrap - const values = new Map([[chain.assetId, wrap.amount]]) - return await estimateEVMCall(api, from, wrap.asset.address, wrap.amount, data, type).then( - (fee) => { - const spending: BaseSpending = new BaseSpending(chain, wrap.amount, chain.assetId) - return new ChainOperationSummary(provider, wrap, chain, fee, [spending, fee.spending], values) - }, - async () => { - const baseFee: bigint = await estimateEVMBaseFee(api) - const transactionData: EVMTransactionData = new EVMTransactionData(from, wrap.asset.address, wrap.amount, data) - const fee: EVMFeeData = new EVMFeeData( - chain, - baseFee * DefaultWrapEstimate, - type, - baseFee, - DefaultWrapEstimate, - transactionData - ) - const spending: BaseSpending = new BaseSpending(chain, wrap.amount, chain.assetId) - return new ChainOperationSummary(provider, wrap, chain, fee, [spending, fee.spending], values) - } - ) -} - -export async function estimateEVMUnwrapOperation ( - provider: MCNProvider, - from: string, - unwrap: UnwrapOperation -): Promise { - const chain: JEVMBlockchain = unwrap.chain - const api: JEVMAPI = provider.jevmApi[chain.id] - const data: string = unwrap.asset.adapter.getWithdrawData(unwrap.amount) - const type: FeeType = FeeType.Unwrap - const values = new Map([[unwrap.asset.assetId, unwrap.amount]]) - return await estimateEVMCall(api, from, unwrap.asset.address, BigInt(0), data, type).then( - (fee) => { - const spending: BaseSpending = new BaseSpending(chain, unwrap.amount, unwrap.asset.assetId) - return new ChainOperationSummary(provider, unwrap, chain, fee, [spending, fee.spending], values) - }, - async () => { - const baseFee: bigint = await estimateEVMBaseFee(api) - const transactionData: EVMTransactionData = new EVMTransactionData(from, unwrap.asset.address, BigInt(0), data) - const fee: EVMFeeData = new EVMFeeData( - chain, - baseFee * DefaultUnwrapEstimate, - type, - baseFee, - DefaultUnwrapEstimate, - transactionData - ) - const spending: BaseSpending = new BaseSpending(chain, unwrap.amount, unwrap.asset.assetId) - return new ChainOperationSummary(provider, unwrap, chain, fee, [spending, fee.spending], values) - } - ) -} - -export async function estimateEVMRedeemAuctionOperation ( - provider: MCNProvider, - from: string, - redeem: RedeemAuctionOperation -): Promise { - const chain: JEVMBlockchain = redeem.chain - const api: JEVMAPI = provider.jevmApi[chain.id] - const adapter: AuctionContractAdapter = new AuctionContractAdapter(redeem.auctionAddress) - const data: string = adapter.getRedeemAuctionData(redeem.auctionId) - const type: FeeType = FeeType.RedeemAuction - return await estimateEVMCall(api, from, redeem.auctionAddress, BigInt(0), data, type).then( - (fee) => { - return new ChainOperationSummary(provider, redeem, chain, fee, [fee.spending], new Map()) - }, - async () => { - const baseFee: bigint = await estimateEVMBaseFee(api) - const transactionData: EVMTransactionData = new EVMTransactionData(from, redeem.auctionAddress, BigInt(0), data) - const fee: EVMFeeData = new EVMFeeData( - chain, - baseFee * DefaultRedeemAuctionEstimate, - type, - baseFee, - DefaultRedeemAuctionEstimate, - transactionData - ) - return new ChainOperationSummary(provider, redeem, chain, fee, [fee.spending], new Map()) - } - ) -} - -export async function estimateEVMWithdrawStreamOperation ( - provider: MCNProvider, - from: string, - withdraw: WithdrawStreamOperation -): Promise { - const chain: JEVMBlockchain = withdraw.chain - const api: JEVMAPI = provider.jevmApi[chain.id] - const adapter: StreamContractAdapter = new StreamContractAdapter(withdraw.streamAddress) - const data: string = adapter.getWithdrawFromStreamData(withdraw.streamId, withdraw.amount) - const type: FeeType = FeeType.WithdrawStream - return await estimateEVMCall(api, from, withdraw.streamAddress, BigInt(0), data, type).then( - (fee) => { - return new ChainOperationSummary(provider, withdraw, chain, fee, [fee.spending], new Map()) - }, - async () => { - const baseFee: bigint = await estimateEVMBaseFee(api) - const transactionData: EVMTransactionData = new EVMTransactionData(from, withdraw.streamAddress, BigInt(0), data) - const fee: EVMFeeData = new EVMFeeData( - chain, - baseFee * DefaultWithdrawStreamEstimate, - type, - baseFee, - DefaultWithdrawStreamEstimate, - transactionData - ) - return new ChainOperationSummary(provider, withdraw, chain, fee, [fee.spending], new Map()) - } - ) -} - -export async function estimateEVMCancelStreamOperation ( - provider: MCNProvider, - from: string, - cancel: CancelStreamOperation -): Promise { - const chain: JEVMBlockchain = cancel.chain - const api: JEVMAPI = provider.jevmApi[chain.id] - const adapter: StreamContractAdapter = new StreamContractAdapter(cancel.streamAddress) - const data: string = adapter.getCancelStreamData(cancel.streamId) - const type: FeeType = FeeType.CancelStream - return await estimateEVMCall(api, from, cancel.streamAddress, BigInt(0), data, type).then( - (fee) => { - return new ChainOperationSummary(provider, cancel, chain, fee, [fee.spending], new Map()) - }, - async () => { - const baseFee: bigint = await estimateEVMBaseFee(api) - const transactionData: EVMTransactionData = new EVMTransactionData(from, cancel.streamAddress, BigInt(0), data) - const fee: EVMFeeData = new EVMFeeData( - chain, - baseFee * DefaultCancelStreamEstimate, - type, - baseFee, - DefaultCancelStreamEstimate, - transactionData - ) - return new ChainOperationSummary(provider, cancel, chain, fee, [fee.spending], new Map()) - } - ) -} - -export async function estimateEthCallOperation ( - provider: MCNProvider, - from: string, - ethCall: EthCallOperation -): Promise { - const chain: JEVMBlockchain = ethCall.chain - const api: JEVMAPI = provider.jevmApi[chain.id] - const contract = new ethers.Contract(ethCall.contract, ethCall.abi, chain.ethProvider) - const data = contract.interface.encodeFunctionData(ethCall.functionName, ethCall.values) - const type: FeeType = FeeType.EthCall - return await estimateEVMCall(api, from, ethCall.contract, ethCall.amount, data, type).then( - (fee) => { - return new ChainOperationSummary(provider, ethCall, chain, fee, [fee.spending], new Map()) - }, - async () => { - const baseFee: bigint = await estimateEVMBaseFee(api) - const transactionData: EVMTransactionData = new EVMTransactionData(from, ethCall.contract, ethCall.amount, data) - const fee: EVMFeeData = new EVMFeeData( - chain, - baseFee * DefaultCancelStreamEstimate, - type, - baseFee, - DefaultCancelStreamEstimate, - transactionData - ) - return new ChainOperationSummary(provider, ethCall, chain, fee, [fee.spending], new Map()) - } - ) -} - export async function estimateEVMWithdrawJRC20 ( api: JEVMAPI, sender: string, jrc20: JRC20Asset, amount: bigint ): Promise { - const type: FeeType = FeeType.Withdraw - const data: string = jrc20.adapter.getWithdrawData(amount) - return await estimateEVMCall(api, sender, jrc20.address, BigInt(0), data, type).catch(async () => { - const baseFee: bigint = await estimateEVMBaseFee(api) - const transactionData: EVMTransactionData = new EVMTransactionData(sender, jrc20.address, BigInt(0), data) - return new EVMFeeData( - api.chain, - baseFee * DefaultWithdrawEstimate, - type, - baseFee, - DefaultWithdrawEstimate, - transactionData - ) - }) + const data = jrc20.adapter.getWithdrawData(amount) + const baseFee = await estimateEVMBaseFee(api) + const fee = new EVMFeeData( + api.chain, + baseFee * DefaultWithdrawEstimate, + FeeType.Withdraw, + baseFee, + DefaultWithdrawEstimate, + new EVMTransactionData(sender, jrc20.address, BigInt(0), data) + ) + await estimateEVMCall(api, sender, jrc20.address, BigInt(0), data, baseFee).then( + (gasLimit) => { + fee.setGasLimit(gasLimit) + }, + () => {} + ) + return fee } export async function estimateEVMDepositJRC20 ( @@ -390,18 +156,21 @@ export async function estimateEVMDepositJRC20 ( jrc20: JRC20Asset, amount: bigint ): Promise { - const type: FeeType = FeeType.Deposit - const data: string = jrc20.adapter.getDepositData(jrc20.nativeAssetId, amount) - return await estimateEVMCall(api, sender, NativeAssetCallContract, BigInt(0), data, type).catch(async () => { - const baseFee: bigint = await estimateEVMBaseFee(api) - const transactionData: EVMTransactionData = new EVMTransactionData(sender, NativeAssetCallContract, BigInt(0), data) - return new EVMFeeData( - api.chain, - baseFee * DefaultDepositEstimate, - type, - baseFee, - DefaultDepositEstimate, - transactionData - ) - }) + const data = jrc20.adapter.getDepositData(jrc20.nativeAssetId, amount) + const baseFee = await estimateEVMBaseFee(api) + const fee = new EVMFeeData( + api.chain, + baseFee * DefaultDepositEstimate, + FeeType.Deposit, + baseFee, + DefaultDepositEstimate, + new EVMTransactionData(sender, NativeAssetCallContract, BigInt(0), data) + ) + await estimateEVMCall(api, sender, NativeAssetCallContract, BigInt(0), data, baseFee).then( + (gasLimit) => { + fee.setGasLimit(gasLimit) + }, + () => {} + ) + return fee } diff --git a/src/wallet/transaction/fee.ts b/src/wallet/transaction/fee.ts index 50674d98..085eb52d 100644 --- a/src/wallet/transaction/fee.ts +++ b/src/wallet/transaction/fee.ts @@ -1,8 +1,8 @@ import { type TokenAsset } from '../../asset' -import { type Blockchain } from '../../chain' +import { type JEVMBlockchain, type Blockchain } from '../../chain' import { type UnsignedTransaction } from '../../transaction' import { type AssetValue } from '../../utils' -import { BaseSpending, type Spending, UtxoSpending } from './transaction' +import { BaseSpending, type EVMTransactionData, UtxoSpending, type Spending } from './transaction' export enum FeeType { Undefined = 'Undefined', @@ -15,9 +15,6 @@ export enum FeeType { Deposit = 'Deposit fee', ValidateFee = 'Validate fee', DelegateFee = 'Delegate fee', - RedeemAuction = 'Redeem fee', - WithdrawStream = 'Withdraw stream fee', - CancelStream = 'Cancel stream fee', CreateSupernet = 'Create supernet fee', RemoveSupernetValidator = 'Remove supernet validator fee', CreateChain = 'Create chain fee', @@ -70,3 +67,28 @@ export class UtxoFeeData extends BaseFeeData { return this.chain.asset.getAssetValue(this.amount) } } + +export class EVMFeeData extends BaseFeeData { + baseFee: bigint + gasLimit: bigint + data: EVMTransactionData + + constructor ( + chain: JEVMBlockchain, + amount: bigint, + type: string, + baseFee: bigint, + gasLimit: bigint, + data: EVMTransactionData + ) { + super(chain, amount, type) + this.baseFee = baseFee + this.gasLimit = gasLimit + this.data = data + } + + setGasLimit (gasLimit: bigint): void { + this.amount = this.baseFee * this.gasLimit + this.gasLimit = gasLimit + } +} diff --git a/src/wallet/transaction/platform.ts b/src/wallet/transaction/platform.ts index 327edc9a..d6d8bdb1 100644 --- a/src/wallet/transaction/platform.ts +++ b/src/wallet/transaction/platform.ts @@ -1,20 +1,20 @@ import { type PlatformBlockchain } from '../../chain' import { type MCNProvider } from '../../juneo' import { - Validator, - type UnsignedTransaction, - NodeId, - buildAddPermissionlessValidatorTransaction, - ProofOfPossession, - PrimarySigner, + Address, buildAddPermissionlessDelegatorTransaction, - buildCreateSupernetTransaction, + buildAddPermissionlessValidatorTransaction, buildAddSupernetValidatorTransaction, - CreateSupernetTransaction, - Address, - buildRemoveSupernetValidatorTransaction, buildCreateChainTransaction, - type DynamicId + buildCreateSupernetTransaction, + buildRemoveSupernetValidatorTransaction, + CreateSupernetTransaction, + type DynamicId, + NodeId, + PrimarySigner, + ProofOfPossession, + type UnsignedTransaction, + Validator } from '../../transaction' import { type PlatformAccount } from '../account' import { diff --git a/src/wallet/transaction/transaction.ts b/src/wallet/transaction/transaction.ts index f1678ccd..70fa6a92 100644 --- a/src/wallet/transaction/transaction.ts +++ b/src/wallet/transaction/transaction.ts @@ -12,9 +12,6 @@ export enum TransactionType { Unwrap = 'Unwrap transaction', PrimaryValidation = 'Primary validation transaction', PrimaryDelegation = 'Primary delegation transaction', - RedeemAuction = 'Redeem auction transaction', - WithdrawStream = 'Withdraw stream transaction', - CancelStream = 'Cancel stream transaction', CreateSupernet = 'Create supernet transaction', ValidateSupernet = 'Supernet validation transaction', RemoveSupernetValidator = 'Remove supernet validator transaction', @@ -62,3 +59,17 @@ export class UtxoSpending extends BaseSpending { this.utxos = utxos } } + +export class EVMTransactionData { + from: string + to: string + value: bigint + data: string + + constructor (from: string, to: string, value: bigint, data: string) { + this.from = from + this.to = to + this.value = value + this.data = data + } +} diff --git a/src/wallet/wallet.ts b/src/wallet/wallet.ts index 33eb7819..1f65d86d 100644 --- a/src/wallet/wallet.ts +++ b/src/wallet/wallet.ts @@ -11,26 +11,16 @@ import { } from '../utils' import * as encoding from '../utils/encoding' -const EVMHdPath = "m/44'/60'/0'/0" -const JVMHdPath = "m/44'/9000'/0'/0" - class NodeManager { mnemonic: string - evmHdWallet: HDNodeWallet - jvmHdWallet: HDNodeWallet constructor (mnemonic: string) { this.mnemonic = mnemonic - this.evmHdWallet = HDNodeWallet.fromPhrase(mnemonic, '', EVMHdPath) - this.jvmHdWallet = HDNodeWallet.fromPhrase(mnemonic, '', JVMHdPath) - } - - deriveEVMPrivateKey (index: number): string { - return this.evmHdWallet.deriveChild(index).privateKey.substring(2) } - deriveJVMPrivateKey (index: number): string { - return this.jvmHdWallet.deriveChild(index).privateKey.substring(2) + derivePrivateKey (chain: Blockchain, index: number): string { + const hdNode = HDNodeWallet.fromPhrase(this.mnemonic, '', `m/44'/${chain.vm.hdPath}'/0'/0`) + return hdNode.deriveChild(index).privateKey.substring(2) } } @@ -77,20 +67,20 @@ export class MCNWallet { } private setChainWallet (chain: Blockchain): void { - if (chain.vmId === JEVM_ID) { + if (chain.vm.id === JEVM_ID) { this.chainsWallets.set(chain.id, this.buildJEVMWallet(chain)) - } else if (chain.vmId === JVM_ID) { + } else if (chain.vm.id === JVM_ID) { this.chainsWallets.set(chain.id, this.buildJVMWallet(chain)) - } else if (chain.vmId === PLATFORMVM_ID) { + } else if (chain.vm.id === PLATFORMVM_ID) { this.chainsWallets.set(chain.id, this.buildJVMWallet(chain)) } else { - throw new WalletError(`unsupported vm id: ${chain.vmId}`) + throw new WalletError(`unsupported vm id: ${chain.vm.id}`) } } private buildJVMWallet (chain: Blockchain): JVMWallet { if (this.nodeManager !== undefined) { - const privateKey = this.nodeManager.deriveJVMPrivateKey(0) + const privateKey = this.nodeManager.derivePrivateKey(chain, 0) return new JVMWallet(privateKey, this.hrp, chain) } else if (this.privateKey !== undefined) { return new JVMWallet(this.privateKey, this.hrp, chain) @@ -100,7 +90,7 @@ export class MCNWallet { private buildJEVMWallet (chain: Blockchain): JEVMWallet { if (this.nodeManager !== undefined) { - const privateKey = this.nodeManager.deriveEVMPrivateKey(0) + const privateKey = this.nodeManager.derivePrivateKey(chain, 0) return new JEVMWallet(privateKey, this.hrp, chain) } else if (this.privateKey !== undefined) { return new JEVMWallet(this.privateKey, this.hrp, chain) diff --git a/tests/e2e/wallet/stake.test.ts b/tests/e2e/wallet/stake.test.ts index 3df5bb0c..936aabc7 100644 --- a/tests/e2e/wallet/stake.test.ts +++ b/tests/e2e/wallet/stake.test.ts @@ -2,7 +2,7 @@ import { AccountError, DecodingError, DelegatePrimaryOperation, - now, + TimeUtils, type ChainAccount, type ExecutableOperation } from '../../../src' @@ -12,13 +12,13 @@ const chainAccount: ChainAccount = ACCOUNT.getAccount(PROVIDER.platformChain.id) // for now we take this nodeID. maybe in the future we can select the node Id with a function const validNodeId = 'NodeID-P6qNB7Zk2tUirf9TvBiXxiCHxa5Hzq6sL' const ONE_DAY: bigint = BigInt(86_400) -let currentTime: bigint = now() + BigInt(30) +let currentTime: bigint = TimeUtils.now() + BigInt(30) let tomorrow: bigint = currentTime + ONE_DAY describe('Staking operations', (): void => { beforeAll(async () => { // TODO create time provider utils to manage those tests - currentTime = now() + BigInt(30) + currentTime = TimeUtils.now() + BigInt(30) tomorrow = currentTime + ONE_DAY })