Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add iota evm #2437

Merged
merged 34 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
6969c27
feat: enable IOTA EVM
Tuditi Mar 22, 2024
94433ad
fix: improve l2->l1 SDR
Tuditi Mar 22, 2024
3f5a5ce
feat: add explorer URLs
Tuditi Apr 15, 2024
5b5759e
Merge remote-tracking branch 'origin/develop' into feat/enable-iota-evm
Tuditi Apr 15, 2024
abefaff
feat: check if explorer URL exists
Tuditi Apr 15, 2024
ebbb057
fix: use correct explorer
Tuditi Apr 15, 2024
a4011f9
feat: use virtual byte cost for calculating buffer
Tuditi Apr 15, 2024
da4f8fb
Merge remote-tracking branch 'origin/develop' into feat/enable-iota-evm
Tuditi Apr 16, 2024
7127be7
fix: multiply gas estimate with gas fee per token
Tuditi Apr 17, 2024
10cd5b1
fix: add IOTA EVM logo
Tuditi Apr 17, 2024
c2d5070
feat: show network initials
Tuditi Apr 17, 2024
91dd02c
Merge remote-tracking branch 'public/develop' into feat/enable-iota-evm
Tuditi Apr 23, 2024
ce43462
chore: interface updates
Tuditi Apr 23, 2024
7942019
feat: support custom networks and handle runtime error
Tuditi Apr 23, 2024
b284a3a
fix: use correct encoding
Tuditi Apr 23, 2024
3f6c0f0
feat: use new explorer URL
Tuditi Apr 24, 2024
096c20d
fix: sending native tokens to L2 for accounts without a balance
Tuditi Apr 24, 2024
0e2ffe2
Merge remote-tracking branch 'public/develop' into feat/enable-iota-evm
Tuditi Apr 29, 2024
37760f6
Merge remote-tracking branch 'origin/develop' into chore/add-iota-evm
Tuditi Apr 30, 2024
40c0b4a
Revert "feat: enable IOTA EVM"
Tuditi May 2, 2024
2c6d6f3
Merge remote-tracking branch 'origin/develop' into chore/add-iota-evm
Tuditi May 2, 2024
f2eb2fe
Merge branch 'develop' into chore/add-iota-evm
nicole-obrien May 3, 2024
b16e0a7
Merge branch 'develop' into chore/add-iota-evm
nicole-obrien May 5, 2024
b60fdd1
fix: type
nicole-obrien May 5, 2024
9766c32
update iota evm colour
nicole-obrien May 5, 2024
a94129a
add evm explorer url
nicole-obrien May 5, 2024
cc43b3d
add evm network coin type
nicole-obrien May 5, 2024
13fed9d
fix evm icon colour
nicole-obrien May 5, 2024
cdd850e
fix: default evm constants
nicole-obrien May 5, 2024
cffb38e
chore: feature flag default isc chains
nicole-obrien May 6, 2024
f5f18d5
cleanup buffer calculation
MarkNerdi May 6, 2024
6f82d8a
Merge branch 'develop' into chore/add-iota-evm
MarkNerdi May 6, 2024
a97f8d7
fix: tests
nicole-obrien May 6, 2024
427a1f7
fix tests
MarkNerdi May 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<script lang="ts">
import {
EvmTransactionData,
L2_TO_L1_STORAGE_DEPOSIT_BUFFER,
calculateEstimatedGasFeeFromTransactionData,
calculateMaxGasFeeFromTransactionData,
getL2ToL1StorageDepositBuffer,
} from '@core/layer-2'
import { Nft, isIrc27Nft } from '@core/nfts'
import { SendFlowParameters, SendFlowType, TokenTransferData } from '@core/wallet'
import { TransactionAssetSection } from '@ui'
import EvmTransactionDetails from './EvmTransactionDetails.svelte'
import { handleError } from '@core/error/handlers'

export let transaction: EvmTransactionData
export let sendFlowParameters: SendFlowParameters
Expand All @@ -27,21 +28,28 @@
}

$: storageDeposit = getTransactionStorageDeposit(sendFlowParameters) ?? BigInt(0)
function getTransactionStorageDeposit(_sendFlowParameters: SendFlowParameters): bigint {
if (_sendFlowParameters.type === SendFlowType.TokenTransfer) {
if (_sendFlowParameters.destinationNetworkId !== _sendFlowParameters.tokenTransfer.token.networkId) {
return BigInt(L2_TO_L1_STORAGE_DEPOSIT_BUFFER[SendFlowType.TokenUnwrap] ?? 0)
}
} else if (_sendFlowParameters.type === SendFlowType.NftTransfer) {
if (
_sendFlowParameters.destinationNetworkId !== _sendFlowParameters.nft?.networkId &&
isIrc27Nft(_sendFlowParameters.nft)
) {
return (
(_sendFlowParameters.nft?.storageDeposit ?? BigInt(0)) +
(L2_TO_L1_STORAGE_DEPOSIT_BUFFER[SendFlowType.NftUnwrap] ?? BigInt(0))
)
function getTransactionStorageDeposit(_sendFlowParameters: SendFlowParameters): bigint | undefined {
try {
const { type, destinationNetworkId } = _sendFlowParameters
if (type === SendFlowType.TokenTransfer) {
if (
destinationNetworkId &&
destinationNetworkId !== _sendFlowParameters?.tokenTransfer?.token.networkId
) {
return getL2ToL1StorageDepositBuffer(SendFlowType.TokenUnwrap, destinationNetworkId)
}
} else if (type === SendFlowType.NftTransfer) {
if (
destinationNetworkId &&
destinationNetworkId !== _sendFlowParameters.nft?.networkId &&
isIrc27Nft(_sendFlowParameters.nft)
) {
const buffer = getL2ToL1StorageDepositBuffer(SendFlowType.NftUnwrap, destinationNetworkId)
return (_sendFlowParameters.nft?.storageDeposit ?? BigInt(0)) + buffer
}
}
} catch (err) {
handleError(err)
}
}
</script>
Expand Down
2 changes: 2 additions & 0 deletions packages/shared/src/components/avatars/NetworkAvatar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
[SupportedNetworkId.Iota]: '#000000',
[SupportedNetworkId.Shimmer]: 'shimmer-background',
[SupportedNetworkId.Testnet]: 'shimmer-background',
[SupportedNetworkId.IotaEvm]: '#0A0FB0',
[SupportedNetworkId.ShimmerEvm]: 'shimmer-background',
[SupportedNetworkId.TestnetEvm]: 'text-secondary',
}
Expand All @@ -21,6 +22,7 @@
[SupportedNetworkId.Iota]: '#FFFFFF',
[SupportedNetworkId.Shimmer]: 'shimmer',
[SupportedNetworkId.Testnet]: 'text-secondary',
[SupportedNetworkId.IotaEvm]: '#FFFFFF',
[SupportedNetworkId.ShimmerEvm]: 'text-invert',
[SupportedNetworkId.TestnetEvm]: 'shimmer-background',
}
Expand Down
6 changes: 6 additions & 0 deletions packages/shared/src/components/avatars/TokenAvatar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
[SupportedNetworkId.Testnet]: {
[BASE_TOKEN_ID]: 'shimmer',
},
[SupportedNetworkId.IotaEvm]: {
[BASE_TOKEN_ID]: '#000000',
},
[SupportedNetworkId.ShimmerEvm]: {
[BASE_TOKEN_ID]: 'shimmer',
},
Expand All @@ -41,6 +44,9 @@
[SupportedNetworkId.Testnet]: {
[BASE_TOKEN_ID]: 'shimmer-background',
},
[SupportedNetworkId.IotaEvm]: {
[BASE_TOKEN_ID]: '#FFFFFF',
},
[SupportedNetworkId.ShimmerEvm]: {
[BASE_TOKEN_ID]: 'shimmer-background',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const DEFAULT_NETWORK_ICON: { [id in NetworkId]?: IconName } = {
[SupportedNetworkId.Iota]: IconName.Iota,
[SupportedNetworkId.Shimmer]: IconName.Shimmer,
[SupportedNetworkId.Testnet]: IconName.Shimmer,
[SupportedNetworkId.IotaEvm]: IconName.Iota,
[SupportedNetworkId.ShimmerEvm]: IconName.Shimmer,
[SupportedNetworkId.TestnetEvm]: IconName.Shimmer,
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export const DEFAULT_TOKEN_ICON: { [networkId in NetworkId]?: { [tokenId: string
[SupportedNetworkId.Testnet]: {
[BASE_TOKEN_ID]: IconName.Shimmer,
},
[SupportedNetworkId.IotaEvm]: {
[BASE_TOKEN_ID]: IconName.Iota,
},
[SupportedNetworkId.ShimmerEvm]: {
[BASE_TOKEN_ID]: IconName.Shimmer,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function checkForUntrackedTokens(account: IAccountState, addPreviouslyUnt
const evmNetworks = getEvmNetworks()
evmNetworks?.forEach(async (evmNetwork) => {
const evmAddress = account.evmAddresses[evmNetwork.coinType]
if (!evmAddress) {
if (!evmAddress || !evmNetwork.explorerUrl) {
return
}
const networkId = evmNetwork.id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,26 @@ export async function getGasFeeForLayer1ToLayer2Transaction(sendFlowParameters:
}

try {
const gasEstimate = await getGasEstimateForIscCall(destinationNetworkId, sendFlowParameters)
return gasEstimate
const gasFeeEstimate = await getGasFeeEstimateForIscCall(destinationNetworkId, sendFlowParameters)
return gasFeeEstimate
} catch (err) {
console.error(err)
return BigInt(FALLBACK_ESTIMATED_GAS[sendFlowParameters.type])
}
}

async function getGasEstimateForIscCall(networkId: NetworkId, sendFlowParameters: SendFlowParameters): Promise<bigint> {
async function getGasFeeEstimateForIscCall(
networkId: NetworkId,
sendFlowParameters: SendFlowParameters
): Promise<bigint> {
const iscChain = getIscChain(networkId)
if (!iscChain) {
return Promise.reject('Invalid network')
}
const account = getSelectedAccount()
const tempOutput = await createStardustOutputFromSendFlowParameters(sendFlowParameters, account)
const outputBytes = await outputHexBytes(tempOutput)
return iscChain.getGasEstimate(outputBytes)
return iscChain.getGasFeeEstimate(outputBytes)
}

function getTransferredAsset(sendFlowParameters: SendFlowParameters): TransferredAsset | undefined {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,22 @@ export function getLayer2MetadataForTransfer(sendFlowParameters: SendFlowParamet
const address = sendFlowParameters.recipient?.address ?? ''
const encodedAddress = encodeAddress(address.toLowerCase(), iscChain)

const estimatedGas = sendFlowParameters.gasFee ?? BigInt(0)
const gasLimit = Math.floor(Number(estimatedGas) * GAS_LIMIT_MULTIPLIER)
const gasFee = sendFlowParameters.gasFee ?? BigInt(0)
const gasPerToken = iscChain.getMetadata()?.gasFeePolicy.gasPerToken
let gasLimit: number
if (gasPerToken) {
// More information can be found here: https://wiki.iota.org/isc/reference/core-contracts/governance/#ratio32
gasLimit = (Number(gasFee) * gasPerToken['a']) / gasPerToken['b']
} else {
gasLimit = Number(gasFee) * GAS_LIMIT_MULTIPLIER
}

metadataStream.writeUInt8('senderContract', EXTERNALLY_OWNED_ACCOUNT)

metadataStream.writeUInt32('targetContract', ACCOUNTS_CONTRACT)
metadataStream.writeUInt32('contractFunction', TRANSFER_ALLOWANCE)
// Gas budget is the ISC equivalent of gas limit in ethereum and what we use throughout the code
metadataStream.writeUInt64SpecialEncoding('gasLimit', BigInt(gasLimit))
metadataStream.writeUInt64SpecialEncoding('gasLimit', BigInt(Math.floor(gasLimit)))

const smartContractParameters = Object.entries({ a: encodedAddress })
const parameters = encodeSmartContractParameters(smartContractParameters)
Expand Down
1 change: 0 additions & 1 deletion packages/shared/src/lib/core/layer-2/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export * from './externally-owned-account.constant'
export * from './fallback-estimated-gas.constant'
export * from './gas-limit-multiplier.constant'
export * from './isc-magic-contract-address.constant'
export * from './l2-to-l1-storage-deposit-buffer.constant'
export * from './layer2-tokens-poll-interval.constant'
export * from './target-contracts.constant'
export * from './transfer-allowance.constant'
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { EvmTransactionData } from '../types'
import { calculateGasFeeInGlow } from '../helpers'

export function calculateEstimatedGasFeeFromTransactionData(transactionData: EvmTransactionData): bigint | undefined {
export function calculateEstimatedGasFeeFromTransactionData(transactionData: EvmTransactionData): bigint {
const { estimatedGas, gasPrice } = transactionData
if (estimatedGas && gasPrice) {
return calculateGasFeeInGlow(estimatedGas, gasPrice)
} else {
return undefined
return BigInt(0)
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { EvmTransactionData } from '../types'
import { calculateGasFeeInGlow } from '../helpers'

export function calculateMaxGasFeeFromTransactionData(transactionData: EvmTransactionData): bigint | undefined {
export function calculateMaxGasFeeFromTransactionData(transactionData: EvmTransactionData): bigint {
const { gasLimit, gasPrice } = transactionData
if (gasLimit && gasPrice) {
return calculateGasFeeInGlow(gasLimit, gasPrice)
} else {
return undefined
return BigInt(0)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ async function getL2NativeTokenBalancesForAddress(evmAddress: string, iscChain:
nativeTokenResult.items?.forEach((item) => (nativeTokens[item.key] = Converter.bigIntLikeToBigInt(item.value)))
return nativeTokens
} catch (e) {
console.error(e)
return {}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { nodeInfo } from '@core/network'
import { DEFAULT_PROTOCOL } from '@core/network/constants'
import { NetworkId } from '@core/network/types'
import { SendFlowType } from '@core/wallet/enums'
import { get } from 'svelte/store'

const L2_TO_L1_STORAGE_DEPOSIT_BUFFER_BYTES: { [key in UnwrapSendFlow]: bigint } = {
[SendFlowType.TokenUnwrap]: BigInt(565),
[SendFlowType.NftUnwrap]: BigInt(35),
}

type UnwrapSendFlow = SendFlowType.TokenUnwrap | SendFlowType.NftUnwrap

export function getL2ToL1StorageDepositBuffer(type: UnwrapSendFlow, network: NetworkId): bigint {
const actualVByteCost = get(nodeInfo)?.protocol.rentStructure.vByteCost
MarkNerdi marked this conversation as resolved.
Show resolved Hide resolved
let expectedVByteCost = DEFAULT_PROTOCOL[network]?.rentStructure.vByteCost
MarkNerdi marked this conversation as resolved.
Show resolved Hide resolved

// TODO: Validate byte cost returned by node
if (!expectedVByteCost && !actualVByteCost) {
throw new Error(`Virtual byte cost for ${network} is undefined!`)
MarkNerdi marked this conversation as resolved.
Show resolved Hide resolved
// } else if (!actualVByteCost) {
// throw new Error('Node does not return virtual byte cost')
} else if (!expectedVByteCost) {
expectedVByteCost = actualVByteCost
}

// if (expectedVByteCost !== actualVByteCost) {
// throw new Error('Virtual byte cost from the node differs from the expected values')
// }

return BigInt(expectedVByteCost) * L2_TO_L1_STORAGE_DEPOSIT_BUFFER_BYTES[type]
}
1 change: 1 addition & 0 deletions packages/shared/src/lib/core/layer-2/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export * from './getErc721TransferSmartContractData'
export * from './getEvmTransactionFromHexString'
export * from './getHexEncodedTransaction'
export * from './getMethodForEvmTransaction'
export * from './getL2ToL1StorageDepositBuffer'
export * from './isErcAsset'
export * from './lookupMethodSignature'
export * from './parseLayer2Metadata'
Expand Down
28 changes: 15 additions & 13 deletions packages/shared/src/lib/core/network/classes/isc-chain.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { fetchIscAssetsForAccount } from '@core/layer-2/utils'
import { NetworkType } from '@core/network/enums'
import { getActiveProfileId } from '@core/profile/stores'
import { ITokenBalance } from '@core/token/interfaces'
import { Converter } from '@core/utils'
import { IIscChain, IIscChainConfiguration, IIscChainMetadata } from '../interfaces'
import { EvmNetwork } from './evm-network.class'

Expand All @@ -27,15 +26,18 @@ export class IscChain extends EvmNetwork implements IIscChain {
this._chainApi = new URL(`v1/chains/${aliasAddress}`, apiEndpoint).href
}

getMetadata(): Promise<IIscChainMetadata> {
if (this._metadata) {
return Promise.resolve(this._metadata)
} else {
this._metadata = <IIscChainMetadata>{} // await this.fetchChainMetadata()
return Promise.resolve(this._metadata)
async setMetadata(): Promise<void> {
try {
this._metadata = await this.fetchChainMetadata()
} catch (err) {
console.error(err)
}
}

getMetadata(): IIscChainMetadata | undefined {
return this._metadata
}

/**
* CAUTION: The API endpoint used by this method is not available
* with the public ShimmerEVM node URL (b/c it's actually just
Expand All @@ -59,9 +61,9 @@ export class IscChain extends EvmNetwork implements IIscChain {
return { ...tokenBalance, ...iscBalance }
}

async getGasEstimate(hex: string): Promise<bigint> {
async getGasFeeEstimate(outputBytes: string): Promise<bigint> {
const URL = `${this._chainApi}/estimategas-onledger`
const body = JSON.stringify({ outputBytes: hex })
const body = JSON.stringify({ outputBytes })

const requestInit: RequestInit = {
method: 'POST',
Expand All @@ -76,12 +78,12 @@ export class IscChain extends EvmNetwork implements IIscChain {
const data = await response.json()

if (response.status === 200) {
const gasEstimate = Converter.bigIntLikeToBigInt(data.gasFeeCharged)
if (gasEstimate === BigInt(0)) {
throw new Error(`Gas fee has an invalid value: ${gasEstimate}!`)
const gasFee = BigInt(data.gasFeeCharged)
if (gasFee === BigInt(0)) {
throw new Error(`Gas fee has an invalid value: ${gasFee}!`)
}

return gasEstimate
return gasFee
} else {
throw new Error(data)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const DEFAULT_BASE_TOKEN: Readonly<{ [id in NetworkId]?: IBaseToken }> =
[SupportedNetworkId.Iota]: IOTA_BASE_TOKEN,
[SupportedNetworkId.Shimmer]: SHIMMER_BASE_TOKEN,
[SupportedNetworkId.Testnet]: SHIMMER_BASE_TOKEN,
[SupportedNetworkId.IotaEvm]: IOTA_BASE_TOKEN,
[SupportedNetworkId.ShimmerEvm]: SHIMMER_BASE_TOKEN,
[SupportedNetworkId.TestnetEvm]: SHIMMER_BASE_TOKEN,
[SupportedNetworkId.Ethereum]: EVM_BASE_TOKEN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ export const SHIMMER_COIN_TYPE = 4219
export const TEST_COIN_TYPE = 1
export const ETHEREUM_COIN_TYPE = 60

export const DEFAULT_COIN_TYPE: Readonly<{ [id in NetworkId]?: number }> = {
export const DEFAULT_COIN_TYPE: Readonly<{ [key in NetworkId]: number }> = {
[SupportedNetworkId.Iota]: IOTA_COIN_TYPE,
[SupportedNetworkId.Shimmer]: SHIMMER_COIN_TYPE,
[SupportedNetworkId.Testnet]: TEST_COIN_TYPE,
[SupportedNetworkId.Ethereum]: ETHEREUM_COIN_TYPE,
[SupportedNetworkId.Sepolia]: ETHEREUM_COIN_TYPE,
[SupportedNetworkId.IotaEvm]: ETHEREUM_COIN_TYPE,
[SupportedNetworkId.ShimmerEvm]: ETHEREUM_COIN_TYPE,
[SupportedNetworkId.TestnetEvm]: ETHEREUM_COIN_TYPE,
[SupportedNetworkId.Ethereum]: ETHEREUM_COIN_TYPE,
[SupportedNetworkId.Sepolia]: ETHEREUM_COIN_TYPE,
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const DEFAULT_EXPLORER_URLS: Readonly<{ [key in NetworkId]?: string }> =
[SupportedNetworkId.Iota]: 'https://explorer.iota.org',
[SupportedNetworkId.Shimmer]: 'https://explorer.shimmer.network',
[SupportedNetworkId.Testnet]: 'https://explorer.shimmer.network',
[SupportedNetworkId.IotaEvm]: 'https://explorer.evm.iota.org',
[SupportedNetworkId.Ethereum]: 'https://eth.blockscout.com',
[SupportedNetworkId.ShimmerEvm]: 'https://explorer.evm.shimmer.network',
[SupportedNetworkId.TestnetEvm]: 'https://explorer.evm.testnet.shimmer.network',
Expand Down
Loading
Loading