Skip to content

Commit

Permalink
fix broken AssetFromString() (#1341)
Browse files Browse the repository at this point in the history
  • Loading branch information
Thorian1te authored Jan 20, 2025
1 parent c62f5a7 commit 590c8eb
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 85 deletions.
7 changes: 7 additions & 0 deletions .changeset/large-ravens-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@xchainjs/xchain-thorchain-query': patch
'@xchainjs/xchain-solana': patch
'@xchainjs/xchain-util': patch
---

fix assetFromString util function
5 changes: 5 additions & 0 deletions .changeset/ninety-toys-shake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@xchainjs/xchain-util': patch
---

Fix broken assetFromString()
4 changes: 4 additions & 0 deletions packages/xchain-solana/__tests__/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ describe('Solana client', () => {
})

describe('Addresses', () => {
let client: Client
beforeAll(() => {
client = new Client()
})
it('Should not get address without phrase', () => {
expect(async () => await client.getAddressAsync()).rejects.toThrowError('Phrase must be provided')
})
Expand Down
2 changes: 1 addition & 1 deletion packages/xchain-solana/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"@metaplex-foundation/mpl-token-metadata": "3.2.1",
"@metaplex-foundation/umi": "0.9.2",
"@metaplex-foundation/umi-bundle-defaults": "0.9.2",
"@solana/addresses": "2.0.0-rc.1",
"@solana/addresses": "2.0.0",
"@solana/spl-token": "0.4.8",
"@solana/web3.js": "1.95.2",
"@xchainjs/xchain-client": "workspace:*",
Expand Down
4 changes: 4 additions & 0 deletions packages/xchain-thorchain-query/src/thorchain-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
AssetCryptoAmount,
Chain,
CryptoAmount,
SECURED_ASSET_DELIMITER,
SYNTH_ASSET_DELIMITER,
SecuredAsset,
SynthAsset,
Expand Down Expand Up @@ -1614,6 +1615,7 @@ export class ThorchainQuery {
['c', 'BCH.BCH'],
['a', 'AVAX.AVAX'],
['s', 'BSC.BNB'],
['f', 'BASE.ETH'],
])

const nativeAsset = nativeAlias.get(alias.toLowerCase())
Expand All @@ -1625,6 +1627,8 @@ export class ThorchainQuery {
delimiter = TRADE_ASSET_DELIMITER
} else if (alias.includes(SYNTH_ASSET_DELIMITER)) {
delimiter = SYNTH_ASSET_DELIMITER
} else if (alias.includes(SECURED_ASSET_DELIMITER)) {
delimiter = SECURED_ASSET_DELIMITER
}

const splitedAlias = alias.split(delimiter)
Expand Down
38 changes: 37 additions & 1 deletion packages/xchain-thorchain/__e2e__/keystore-client.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { Tx } from '@xchainjs/xchain-client'
import { assetAmount, assetFromStringEx, assetToBase, assetToString, baseToAsset } from '@xchainjs/xchain-util'
import {
AssetType,
SecuredAsset,
assetAmount,
assetFromStringEx,
assetToBase,
assetToString,
baseToAsset,
} from '@xchainjs/xchain-util'

import { AssetRuneNative as AssetRune, Client, DepositTx } from '../src'

Expand Down Expand Up @@ -47,6 +55,12 @@ const getPrintableDepositTx = (depositTx: DepositTx) => {
}),
}
}
const BTCSECURED: SecuredAsset = {
chain: 'BTC',
ticker: 'BTC',
symbol: 'BTC',
type: AssetType.SECURED,
}

describe('Thorchain Keystore', () => {
let client: Client
Expand Down Expand Up @@ -143,6 +157,28 @@ describe('Thorchain Keystore', () => {
}
})

it('Should make secured Asset swap', async () => {
try {
/**
* MAKE SURE TO TEST THIS FUNCTION WITH YOUR ADDRESS BNB, OTHERWISE, YOU COULD LOSE FUNDS
*/
const address: string = 'thor1rkpukrhljr72sxww2t0nwvng84zegp59805e03' || 'TO_BE_DEFINED'
if (address === 'TO_BE_DEFINED') throw Error('Set an address to try the deposit e2e function')
const memo = `=:AVAX-AVAX:${address}`

const hash = await client.deposit({
walletIndex: 0,
amount: assetToBase(assetAmount(0.00024005, 8)),
asset: BTCSECURED,
memo,
})
console.log(hash)
} catch (error) {
console.log(error)
throw error
}
})

it('Should transfer offline', async () => {
const txRaw = await client.transferOffline({
walletIndex: 0,
Expand Down
37 changes: 31 additions & 6 deletions packages/xchain-util/__tests__/asset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,15 +343,35 @@ describe('asset', () => {
expect(result).toEqual({ chain: 'ETH', symbol: 'ETH', ticker: 'ETH', type: AssetType.TRADE })
})

it('trade BTC-BTC', () => {
it('secured BTC-BTC', () => {
const result = assetFromString('BTC-BTC')
expect(result).toEqual({ chain: 'BTC', symbol: 'BTC', ticker: 'BTC', type: AssetType.SECURED })
})
it('trade ETH-ETH', () => {
it('secured ETH-ETH', () => {
const result = assetFromString('ETH-ETH')
expect(result).toEqual({ chain: 'ETH', symbol: 'ETH', ticker: 'ETH', type: AssetType.SECURED })
})

it('secured AVAX-SOL-0XFE6B19286885A4F7F55ADAD09C3CD1F906D2478', () => {
const result = assetFromString('AVAX-SOL-0XFE6B19286885A4F7F55ADAD09C3CD1F906D2478')
expect(result).toEqual({
chain: 'AVAX',
symbol: 'SOL-0XFE6B19286885A4F7F55ADAD09C3CD1F906D2478',
ticker: 'SOL',
type: AssetType.SECURED,
})
})

it('synth ETH/USDC-0XA0B86991C6218B36C1D19D4A2E9EB0CE3606EB48', () => {
const result = assetFromString('ETH/USDC-0XA0B86991C6218B36C1D19D4A2E9EB0CE3606EB48')
expect(result).toEqual({
chain: 'ETH',
symbol: 'USDC-0XA0B86991C6218B36C1D19D4A2E9EB0CE3606EB48',
ticker: 'USDC',
type: AssetType.SYNTH,
})
})

it('KUJI.USK', () => {
const result = assetFromString('KUJI.USK')
expect(result).toEqual({ chain: 'KUJI', symbol: 'USK', ticker: 'USK', type: AssetType.TOKEN })
Expand Down Expand Up @@ -398,10 +418,6 @@ describe('asset', () => {
const result = assetFromString('.BNB.BNB')
expect(result).toBeNull()
})
it('null for invalid chain', () => {
const result = assetFromString('invalid.BNB.BNB')
expect(result).toEqual({ chain: 'invalid', symbol: 'BNB', type: AssetType.NATIVE, ticker: 'BNB' })
})
})

describe('assetToString', () => {
Expand All @@ -413,6 +429,15 @@ describe('asset', () => {
const asset: Asset = { chain: 'ETH', symbol: 'ETH', ticker: 'ETH', type: AssetType.NATIVE }
expect(assetToString(asset)).toEqual('ETH.ETH')
})
it('DAI string test', () => {
const asset: TokenAsset = {
chain: 'ETH',
symbol: 'DAI-0X6B175474E89094C44DA98B954EEDEAC495271D0F',
ticker: 'DAI',
type: AssetType.TOKEN,
}
expect(assetToString(asset)).toEqual('ETH.DAI-0X6B175474E89094C44DA98B954EEDEAC495271D0F')
})
it('ETH/ETH', () => {
const asset: SynthAsset = { chain: 'ETH', symbol: 'ETH', ticker: 'ETH', type: AssetType.SYNTH }
expect(assetToString(asset)).toEqual('ETH/ETH')
Expand Down
52 changes: 35 additions & 17 deletions packages/xchain-util/src/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,41 +278,59 @@ const assetConfigs = new Map<string, AnyAsset>([
['RUNE', { chain: 'THOR', symbol: 'RUNE', ticker: 'RUNE', type: AssetType.NATIVE }],
])

const createAsset = (chain: string, symbol: string, ticker: string, type: AssetType) => {
// Helper function to create an asset from its components
const createAsset = (chain: string, symbol: string, ticker: string, type: AssetType): AnyAsset => {
return { chain, symbol, ticker, type }
}

export const assetFromString = (s: string): AnyAsset | null => {
if (!s || s.trim() === '') return null // Handle empty strings

// Check if the asset is directly in the assetConfigs
const directAsset = assetConfigs.get(s)
if (directAsset) return directAsset

// Define possible delimiters and their associated asset types
const delimiters: Record<'.' | '/' | '~' | '-', AssetType> = {
// Define asset delimiters for the first delimiter
const assetDelimiters: Record<string, AssetType> = {
'.': AssetType.NATIVE,
'/': AssetType.SYNTH,
'~': AssetType.TRADE,
'-': AssetType.SECURED,
}

// Identify the first delimiter
const delimiter = Object.keys(delimiters).find((delim) => s.includes(delim)) as '.' | '/' | '~' | '-' | undefined
if (!delimiter) return null
// Identify the first delimiter in the string
const firstDelimiter = Object.keys(assetDelimiters).find((delim) => s.includes(delim)) as
| keyof typeof assetDelimiters
| undefined
if (!firstDelimiter) return null

// Split the string by the first delimiter
const [chain, symbol] = s.split(delimiter)
if (!chain || !symbol) return null
// Split the string into chain and symbol part based on the first delimiter
const [chain, ...restParts] = s.split(firstDelimiter)

// Determine initial type based on delimiter
let type = delimiters[delimiter]
if (!chain || restParts.length === 0) return null // Invalid format or empty parts

// Additional checks for token classification
if (delimiter === '.' && symbol.includes('-')) {
type = AssetType.TOKEN
}
// Handle secured and trade assets which may have further splits
const symbol = restParts.join(firstDelimiter)
let ticker = symbol.split('-')[0]

// Extract the ticker from the symbol (first part before `-` if it exists)
const ticker = symbol.split('-')[0]
// For secured and trade assets, handle contract address as part of the symbol
const type = assetDelimiters[firstDelimiter]

// Check if symbol is empty (e.g., BNB. or AVAX~ cases)
if (symbol === '') return null
// Handle trade and secured assets
if (firstDelimiter === '~' || firstDelimiter === '-' || firstDelimiter === '/') {
return createAsset(chain.trim(), symbol.trim(), ticker.trim(), type)
}
// Handle token assets: if the symbol has more than one part (split by `-`)
if (symbol.includes('-')) {
const [primaryTicker] = symbol.split('-')
ticker = primaryTicker

// For token assets, use the contract address
return createAsset(chain.trim(), symbol.trim(), ticker.trim(), AssetType.TOKEN)
}
// For native, synth, or other types of assets
return createAsset(chain.trim(), symbol.trim(), ticker.trim(), type)
}

Expand Down
Loading

0 comments on commit 590c8eb

Please sign in to comment.