diff --git a/packages/xc-cfg/src/resolvers/hydration.ts b/packages/xc-cfg/src/resolvers/hydration.ts index acb7ba44..8eb81301 100644 --- a/packages/xc-cfg/src/resolvers/hydration.ts +++ b/packages/xc-cfg/src/resolvers/hydration.ts @@ -1,18 +1,19 @@ import { EvmResolver } from '@galacticcouncil/xc-core'; -import { getSs58AddressInfo } from '@polkadot-api/substrate-bindings'; +import { h160 } from '@galacticcouncil/common'; -const ETH_PREFIX = 'ETH\0'; +const { H160, isEvmAccount } = h160; export class HydrationEvmResolver implements EvmResolver { async toH160(ss58Addr: string): Promise { - const info = getSs58AddressInfo(ss58Addr); - if (!info.isValid) { - throw new Error(`Invalid SS58 address: ${ss58Addr}`); + if (!isEvmAccount(ss58Addr)) { + throw new Error( + `SS58 address is not an EVM-derived account: ${ss58Addr}` + ); } + return H160.fromAccount(ss58Addr); + } - const decodedBytes = info.publicKey; - const prefixBytes = Buffer.from(ETH_PREFIX); - const addressBytes = decodedBytes.slice(prefixBytes.length, -8); - return '0x' + Buffer.from(addressBytes).toString('hex'); + async toSS58(h160Addr: string): Promise { + return H160.toAccount(h160Addr); } } diff --git a/packages/xc-core/src/chain/EvmParachain.ts b/packages/xc-core/src/chain/EvmParachain.ts index 79f2dca8..48c000bd 100644 --- a/packages/xc-core/src/chain/EvmParachain.ts +++ b/packages/xc-core/src/chain/EvmParachain.ts @@ -7,7 +7,7 @@ import { Wormhole, WormholeDef } from '../bridge'; import { EvmClient, EvmResolver } from '../evm'; import { addr } from '../utils'; -const { EvmAddr } = addr; +const { EvmAddr, Ss58Addr } = addr; export interface EvmParachainParams extends ParachainParams { evmChain: EvmChainDef; @@ -52,6 +52,19 @@ export class EvmParachain extends Parachain { if (this.evmResolver) { return this.evmResolver.toH160(address, this.client); } - throw new Error(`No EVM resolver found for ` + this.name); + + return address; + } + + async getSubstrateAddress(address: string): Promise { + if (Ss58Addr.isValid(address)) { + return address; + } + + if (this.evmResolver) { + return this.evmResolver.toSS58(address, this.client); + } + + return address; } } diff --git a/packages/xc-core/src/evm/EvmResolver.ts b/packages/xc-core/src/evm/EvmResolver.ts index cccee8ad..ba36441e 100644 --- a/packages/xc-core/src/evm/EvmResolver.ts +++ b/packages/xc-core/src/evm/EvmResolver.ts @@ -2,4 +2,5 @@ import { PolkadotClient } from 'polkadot-api'; export interface EvmResolver { toH160(ss58Addr: string, api?: PolkadotClient): Promise; + toSS58(h160Addr: string, api?: PolkadotClient): Promise; } diff --git a/packages/xc-sdk/src/Wallet.ts b/packages/xc-sdk/src/Wallet.ts index c0fb2b2a..ccce52c1 100644 --- a/packages/xc-sdk/src/Wallet.ts +++ b/packages/xc-sdk/src/Wallet.ts @@ -27,9 +27,9 @@ import { SubstrateService, } from './platforms'; import { + buildBalanceConfig, calculateMax, calculateMin, - formatEvmAddress, DataOriginProcessor, DataReverseProcessor, } from './transfer'; @@ -37,8 +37,6 @@ import { Transfer } from './types'; import { FeeSwap } from './FeeSwap'; -const { EvmAddr } = addr; - export interface WalletOptions { configService: ConfigService; transferValidations?: TransferValidation[]; @@ -264,15 +262,12 @@ export class Wallet { .getUniqueRoutes() .map(async ({ source }) => { const { asset, balance } = source; - const assetId = chainRoutes.chain.getBalanceAssetId(asset); - const account = EvmAddr.isValid(assetId.toString()) - ? await formatEvmAddress(address, chainRoutes.chain) - : address; - const balanceConfig = balance.build({ - address: account, - asset: asset, - chain: chainRoutes.chain, - }); + const balanceConfig = await buildBalanceConfig( + balance, + address, + asset, + chainRoutes.chain + ); return adapter.subscribeBalance(asset, balanceConfig); }); diff --git a/packages/xc-sdk/src/transfer/DataOriginProcessor.ts b/packages/xc-sdk/src/transfer/DataOriginProcessor.ts index 75a142ac..8c7a5861 100644 --- a/packages/xc-sdk/src/transfer/DataOriginProcessor.ts +++ b/packages/xc-sdk/src/transfer/DataOriginProcessor.ts @@ -2,6 +2,7 @@ import { addr, Asset, AssetAmount, + CallType, FeeAmountConfigBuilder, FeeAssetConfigBuilder, Parachain, @@ -12,22 +13,20 @@ import { } from '@galacticcouncil/xc-core'; import { acc, big } from '@galacticcouncil/common'; -import { formatAmount, formatEvmAddress } from './utils'; +import { buildBalanceConfig, formatAmount, resolveAddress } from './utils'; import { Call, PlatformAdapter, SubstrateService } from '../platforms'; import { DataProcessor } from './DataProcessor'; -const { EvmAddr } = addr; - export class DataOriginProcessor extends DataProcessor { constructor(adapter: PlatformAdapter, config: TransferConfig) { super(adapter, config); } async getCall(ctx: TransferCtx): Promise { - const { amount, sender, source } = ctx; - const transfer = await this.getTransfer(ctx); - return this.adapter.buildCall(sender, amount, source.feeBalance, transfer); + const { amount, source } = ctx; + const { transfer, account } = await this.getTransfer(ctx); + return this.adapter.buildCall(account, amount, source.feeBalance, transfer); } async getDestinationFee(): Promise<{ @@ -82,35 +81,28 @@ export class DataOriginProcessor extends DataProcessor { return this.getBalance(address); } - const feeAssetId = chain.getBalanceAssetId(feeAsset); - const account = EvmAddr.isValid(feeAssetId.toString()) - ? await formatEvmAddress(address, chain) - : address; - const feeBalanceConfig = source.destinationFee.balance.build({ - address: account, - asset: feeAsset, - chain: chain, - }); + const feeBalanceConfig = await buildBalanceConfig( + source.destinationFee.balance, + address, + feeAsset, + chain + ); return this.adapter.getBalance(feeAsset, feeBalanceConfig); } async getFee(ctx: TransferCtx): Promise { - const { chain, route } = this.config; - const { amount, sender, source } = ctx; + const { amount, source } = ctx; - const transfer = await this.getTransfer(ctx); - const address = route.contract - ? await formatEvmAddress(sender, chain) - : sender; + const { transfer, account } = await this.getTransfer(ctx); const networkFee = await this.adapter.estimateFee( - address, + account, amount, source.feeBalance, transfer ); - const { fee } = route.source; + const { fee } = this.config.route.source; const extraFee = fee ? formatAmount(networkFee.decimals, fee.extra) : 0n; const totalFee = networkFee.amount + extraFee; return networkFee.copyWith({ amount: totalFee }); @@ -124,17 +116,18 @@ export class DataOriginProcessor extends DataProcessor { return this.getBalance(address); } - const feeAsset = await this.getFeeAsset(address); - const feeAssetId = chain.getBalanceAssetId(feeAsset); - const account = EvmAddr.isValid(feeAssetId.toString()) - ? await formatEvmAddress(address, chain) - : address; - const feeBalanceConfig = source.fee.balance.build({ - address: account, - asset: feeAsset, - chain: chain, - }); - + const substrateAddr = await resolveAddress( + address, + chain, + CallType.Substrate + ); + const feeAsset = await this.getFeeAsset(substrateAddr); + const feeBalanceConfig = await buildBalanceConfig( + source.fee.balance, + address, + feeAsset, + chain + ); return this.adapter.getBalance(feeAsset, feeBalanceConfig); } @@ -167,23 +160,25 @@ export class DataOriginProcessor extends DataProcessor { if (extrinsic) { const { address, amount, asset, sender } = ctx; const substrate = await SubstrateService.create(chain as Parachain); + const account = await resolveAddress(sender, chain, CallType.Substrate); const messageId = await substrate.buildMessageId( - sender, + account, amount, asset.originSymbol, address ); - return extrinsic.build({ + const transfer = await extrinsic.build({ ...ctx, messageId: messageId, }); + return { transfer, account }; } const callable = contract || program || move; if (callable) { - return callable.build({ - ...ctx, - }); + const transfer = await callable.build({ ...ctx }); + const account = await resolveAddress(ctx.sender, chain, transfer.type); + return { transfer, account }; } throw new Error( @@ -234,7 +229,11 @@ export class DataOriginProcessor extends DataProcessor { const submittable = config.getTx(substrate.client); const fromChain = ctx.source.chain as Parachain; - const fromAddr = ctx.sender; + const fromAddr = await resolveAddress( + ctx.sender, + fromChain, + CallType.Substrate + ); const mda = acc.getMultilocationDerivatedAccount( fromChain.parachainId, @@ -292,11 +291,12 @@ export class DataOriginProcessor extends DataProcessor { ): Promise { const { chain } = this.config; const { fee } = cfg; - const feeBalanceConfig = fee.balance.build({ - address: ctx.sender, - asset: fee.asset, - chain: chain, - }); + const feeBalanceConfig = await buildBalanceConfig( + fee.balance, + ctx.sender, + fee.asset, + chain + ); return this.adapter.getBalance(fee.asset, feeBalanceConfig); } } diff --git a/packages/xc-sdk/src/transfer/DataProcessor.ts b/packages/xc-sdk/src/transfer/DataProcessor.ts index b46f8f57..f3dabb0f 100644 --- a/packages/xc-sdk/src/transfer/DataProcessor.ts +++ b/packages/xc-sdk/src/transfer/DataProcessor.ts @@ -1,5 +1,4 @@ import { - addr, Asset, AssetAmount, Parachain, @@ -7,11 +6,9 @@ import { } from '@galacticcouncil/xc-core'; import { big } from '@galacticcouncil/common'; -import { formatEvmAddress } from './utils'; +import { buildBalanceConfig } from './utils'; import { PlatformAdapter, SubstrateService } from '../platforms'; -const { EvmAddr } = addr; - export abstract class DataProcessor { readonly adapter: PlatformAdapter; readonly config: TransferConfig; @@ -36,17 +33,12 @@ export abstract class DataProcessor { const { source } = route; const asset = source.asset; - - const assetId = chain.getBalanceAssetId(asset); - const account = EvmAddr.isValid(assetId.toString()) - ? await formatEvmAddress(address, chain) - : address; - const balanceConfig = route.source.balance.build({ - address: account, - asset: asset, - chain: chain, - }); - + const balanceConfig = await buildBalanceConfig( + route.source.balance, + address, + asset, + chain + ); return this.adapter.getBalance(asset, balanceConfig); } diff --git a/packages/xc-sdk/src/transfer/utils.ts b/packages/xc-sdk/src/transfer/utils.ts index cdfc9988..c040958f 100644 --- a/packages/xc-sdk/src/transfer/utils.ts +++ b/packages/xc-sdk/src/transfer/utils.ts @@ -1,4 +1,11 @@ -import { AnyChain, AssetAmount, EvmParachain } from '@galacticcouncil/xc-core'; +import { + AnyChain, + Asset, + AssetAmount, + BalanceConfigBuilder, + CallType, + EvmParachain, +} from '@galacticcouncil/xc-core'; import { big } from '@galacticcouncil/common'; import Big from 'big.js'; @@ -74,23 +81,47 @@ export function calculateMin( } /** - * Return h160 derivated address in case of Evm parachain + * Resolve address to the format required by the given CallType + * on dual-address-space (EvmParachain) chains. + * * * @param address - h160 or ss58 address format * @param chain - transfer chain ctx - * @returns - derivated address if evm resolver defined, fallback to default + * @param callType - target format needed (Evm or Substrate) + * @returns address in the correct format */ -export async function formatEvmAddress( +export async function resolveAddress( address: string, - chain: AnyChain + chain: AnyChain, + callType: CallType ): Promise { if (chain.isEvmParachain()) { const evmParachain = chain as EvmParachain; - return evmParachain.getDerivatedAddress(address); + return callType === CallType.Evm + ? evmParachain.getDerivatedAddress(address) + : evmParachain.getSubstrateAddress(address); } return address; } +/** + * Build a balance config with the address resolved to the format + * required by the config's CallType. + */ +export async function buildBalanceConfig( + builder: BalanceConfigBuilder, + address: string, + asset: Asset, + chain: AnyChain +) { + const config = builder.build({ address, asset, chain }); + const account = await resolveAddress(address, chain, config.type); + if (account === address) { + return config; + } + return builder.build({ address: account, asset, chain }); +} + /** * Format amount if defined *