Skip to content

Commit

Permalink
feat: migrate over @fuels/assets package into the TS SDK (FuelLabs#1747)
Browse files Browse the repository at this point in the history
* feat: migrate over @fuels/assets package into the TS SDK

* add tests, add some docstrings

* Update tsup.config.ts

* Update network.test.ts

* Update resolveIconPaths.test.ts

* Update url.test.ts

* updater test docstrings

* Update tweaking-the-blockchain.test.ts

* fixup

---------

Co-authored-by: Anderson Arboleya <[email protected]>
  • Loading branch information
Dhaiwat10 and arboleya authored Feb 20, 2024
1 parent 9dc6a83 commit 116d642
Show file tree
Hide file tree
Showing 18 changed files with 411 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/loud-rocks-walk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fuel-ts/account": patch
---

feat: migrate over @fuels/assets package into the TS SDK
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ apps/demo-react-vite

packages/fuels/test/fixtures/project
packages/account/src/providers/__generated__
packages/account/src/providers/assets

out
CHANGELOG.md
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,25 @@ jobs:
git commit -m "docs: API docs - v${{ env.BUILD_VERSION }}"
git push
git restore apps/docs/.gitignore
# Upload assets to S3
- uses: unfor19/[email protected]
if: steps.changesets.outputs.published != 'true'
with:
version: 2
verbose: false
arch: amd64
rootdir: ""
workdir: ""
- uses: aws-actions/configure-aws-credentials@v1
if: steps.changesets.outputs.published != 'true'
with:
aws-access-key-id: ${{ secrets.S3_CDN_ACCESS_KEY }}
aws-secret-access-key: ${{ secrets.S3_CDN_SECRET_KEY }}
aws-region: us-east-1
- name: Upload assets to s3
if: steps.changesets.outputs.published != 'true'
run: |
aws s3 cp ./packages/account/src/providers/assets/images/ s3://${S3_CDN_BUCKET}/assets/ --recursive
env:
S3_CDN_BUCKET: ${{ secrets.S3_CDN_BUCKET }}
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ apps/demo-typegen/src/predicate-types
apps/docs/.vitepress/cache/

packages/fuels/test/fixtures/project
packages/account/src/providers/assets

__generated__
out
Expand Down
1 change: 1 addition & 0 deletions packages/account/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/providers/assets/images/assets.json
18 changes: 18 additions & 0 deletions packages/account/src/providers/assets/images/eth.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 37 additions & 0 deletions packages/account/src/providers/assets/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { CHAIN_IDS } from '../chains';

import type { Assets } from './types';

export const assets: Assets = [
{
name: 'Ethereum',
symbol: 'ETH',
icon: 'eth.svg',
networks: [
{
type: 'ethereum',
chainId: CHAIN_IDS.eth.sepolia,
decimals: 18,
},
{
type: 'ethereum',
chainId: CHAIN_IDS.eth.foundry,
decimals: 18,
},
{
type: 'fuel',
chainId: CHAIN_IDS.fuel.beta5,
decimals: 9,
assetId: '0x0000000000000000000000000000000000000000000000000000000000000000',
},
{
type: 'fuel',
chainId: CHAIN_IDS.fuel.devnet,
decimals: 9,
assetId: '0x0000000000000000000000000000000000000000000000000000000000000000',
},
],
},
];

export * from './utils';
39 changes: 39 additions & 0 deletions packages/account/src/providers/assets/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export type Ethereum = {
/** type of network */
type: 'ethereum';
/** chain id of the network */
chainId: number;
/** number of decimals of the asset */
decimals: number;
/** address of the asset contract */
address?: string;
};

export type Fuel = {
/** type of network */
type: 'fuel';
/** chain id of the network */
chainId: number;
/** number of decimals of the asset */
decimals: number;
/** assetId on the Fuel Network */
assetId: string;
/** the contractId of that generated the Asset on the Fuel Network */
contractId?: string;
};

export type Asset = {
/** name of the asset */
name: string;
/** description of the asset */
symbol: string;
/** icon of the asset */
icon: string;
/** asset id on Fuel Network */
networks: Array<Ethereum | Fuel>;
};

export type Assets = Array<Asset>;

export type AssetEth = Omit<Asset, 'networks'> & Ethereum;
export type AssetFuel = Omit<Asset, 'networks'> & Fuel;
3 changes: 3 additions & 0 deletions packages/account/src/providers/assets/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './network';
export * from './resolveIconPaths';
export * from './url';
75 changes: 75 additions & 0 deletions packages/account/src/providers/assets/utils/network.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Asset } from '../types'
import { getAssetEth, getAssetFuel, getAssetWithNetwork, getDefaultChainId } from '../utils/network';
import { assets } from '../index'
import { CHAIN_IDS } from '../../chains'

/**
* @group node
*/
describe('Network Utils', () => {
test('getDefaultChainId', async () => {
expect(getDefaultChainId('ethereum')).toBe(11155111);
expect(getDefaultChainId('fuel')).toBe(0);
})

test('getAssetWithNetwork - Ethereum', async () => {
const asset = assets[0] as Asset
const assetEth = getAssetWithNetwork({ asset, networkType: 'ethereum', chainId: CHAIN_IDS.eth.sepolia })
expect(assetEth).toEqual({
type: 'ethereum',
chainId: CHAIN_IDS.eth.sepolia,
decimals: 18,
icon: 'eth.svg',
name: 'Ethereum',
symbol: 'ETH'
})
})

test('getAssetWithNetwork - Fuel', async () => {
const asset = assets[0] as Asset
const assetFuel = getAssetWithNetwork({ asset, networkType: 'fuel', chainId: CHAIN_IDS.fuel.beta5 })
expect(assetFuel).toEqual({
type: 'fuel',
chainId: CHAIN_IDS.fuel.beta5,
decimals: 9,
assetId: '0x0000000000000000000000000000000000000000000000000000000000000000',
icon: 'eth.svg',
name: 'Ethereum',
symbol: 'ETH'
})
})

test('getAssetWithNetwork - invalid network', async () => {
const asset = assets[0] as Asset
const assetUndefined = getAssetWithNetwork({ asset, networkType: 'ethereum', chainId: 0 })
expect(assetUndefined).toBeUndefined()
})

test('getAssetEth', async () => {
const asset = assets[0] as Asset
const assetEth = getAssetEth(asset)
expect(assetEth).toEqual({
type: 'ethereum',
chainId: CHAIN_IDS.eth.sepolia,
decimals: 18,
icon: 'eth.svg',
name: 'Ethereum',
symbol: 'ETH',
})
})

test('getAssetFuel', async () => {
const asset = assets[0] as Asset
const assetFuel = getAssetFuel(asset)

expect(assetFuel).toEqual({
type: 'fuel',
chainId: CHAIN_IDS.fuel.beta5,
decimals: 9,
assetId: '0x0000000000000000000000000000000000000000000000000000000000000000',
icon: 'eth.svg',
name: 'Ethereum',
symbol: 'ETH',
})
})
})
95 changes: 95 additions & 0 deletions packages/account/src/providers/assets/utils/network.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { CHAIN_IDS } from '../../chains';
import type { Asset, AssetEth, AssetFuel, Ethereum, Fuel } from '../types';

type Network = Ethereum | Fuel;
export type NetworkTypes = Ethereum['type'] | Fuel['type'];
type NetworkTypeToNetwork<T> = T extends 'ethereum' ? Ethereum : T extends 'fuel' ? Fuel : Network;

/**
* Returns the default chainId for the given network
*/
export const getDefaultChainId = (networkType: NetworkTypes): number | undefined => {
if (networkType === 'ethereum') {
return CHAIN_IDS.eth.sepolia;
}
if (networkType === 'fuel') {
return CHAIN_IDS.fuel.beta5;
}

return undefined;
};

export type GetAssetNetworkParams<T extends NetworkTypes | undefined> = {
asset: Asset;
chainId?: number;
networkType: T;
};

/**
* Returns the asset's network on the given network
* eg. getAssetNetwork({ asset, chainId: 1, networkType: 'ethereum' }) will return the asset's details on Ethereum mainnet
*/
export const getAssetNetwork = <T extends NetworkTypes | undefined>({
asset,
chainId,
networkType,
}: GetAssetNetworkParams<T>): NetworkTypeToNetwork<T> => {
const network = asset.networks.find(
(item) => item.chainId === chainId && item.type === networkType
) as NetworkTypeToNetwork<T>;

return network;
};

/**
* Returns the asset's details on the given network alongwith the asset itself
* eg. getAssetWithNetwork({ asset, chainId: 1, networkType: 'ethereum' }) will return the asset's details on Ethereum mainnet and the asset itself
*/
export const getAssetWithNetwork = <T extends NetworkTypes>({
asset,
chainId,
networkType,
}: GetAssetNetworkParams<T>): AssetEth | AssetFuel | undefined => {
const { networks: _, ...assetRest } = asset;

const chainIdToUse = chainId ?? getDefaultChainId(networkType);
// use two equals(==) cuz we wan't to keep 0 as a valid chainId
if (chainIdToUse === undefined) {
return undefined;
}

const assetNetwork = getAssetNetwork({
asset,
chainId: chainIdToUse,
networkType,
});

if (!assetNetwork) {
return undefined;
}

return {
...assetRest,
...assetNetwork,
};
};

/**
* Returns the asset's details on Ethereum
*/
export const getAssetEth = (asset: Asset, chainId?: number): AssetEth | undefined =>
getAssetWithNetwork({
asset,
networkType: 'ethereum',
chainId,
}) as AssetEth;

/**
* Returns the asset's details on Fuel
*/
export const getAssetFuel = (asset: Asset, chainId?: number): AssetFuel | undefined =>
getAssetWithNetwork({
asset,
networkType: 'fuel',
chainId,
}) as AssetFuel;
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { resolveIconPaths } from './resolveIconPaths';
import { assets } from '..';

/**
* @group node
*/
describe('resolveIconPaths', () => {
test('without basePath', () => {
const result = resolveIconPaths(assets);

assets.forEach((asset, index) => {
expect(result[index].icon).toBe(`./${asset.icon}`);
});
});

test('with basePath', () => {
const result = resolveIconPaths(assets, 'https://some-url.com');

assets.forEach((asset, index) => {
expect(result[index].icon).toBe(`https://some-url.com/${asset.icon}`);
});
});
});
16 changes: 16 additions & 0 deletions packages/account/src/providers/assets/utils/resolveIconPaths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { Assets } from '../types';

import { urlJoin } from './url';

/**
* Returns the list of assets with the icon paths 'resolved'. eg. `./eth.svg` -> `https://some-url.com/eth.svg`
* @param assets - List of assets
* @param basePath - Base path for the icon URLs (default: './')
* @returns The assets with the icon paths resolved
*/
export function resolveIconPaths(assets: Assets, basePath = './') {
return assets.map((asset) => ({
...asset,
icon: urlJoin(basePath, asset.icon),
}));
}
Loading

0 comments on commit 116d642

Please sign in to comment.