Skip to content

Commit cc9c5f3

Browse files
feat: support NFT assets
1 parent 13d4d53 commit cc9c5f3

33 files changed

Lines changed: 17314 additions & 131 deletions

.github/workflows/build-lint-test.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ on:
2121
required: true
2222
SECURITY_ALERTS_API_BASE_URL:
2323
required: true
24+
NFT_API_BASE_URL:
25+
required: true
2426
EXPLORER_BASE_URL:
2527
required: true
2628

@@ -33,6 +35,8 @@ jobs:
3335
uses: MetaMask/action-checkout-and-setup@v1
3436
with:
3537
is-high-risk-environment: false
38+
node-version-file: ".nvmrc"
39+
cache: "yarn"
3640
- name: Install Yarn dependencies
3741
run: yarn --immutable
3842

@@ -53,11 +57,14 @@ jobs:
5357
TOKEN_API_BASE_URL: ${{ secrets.TOKEN_API_BASE_URL }}
5458
STATIC_API_BASE_URL: ${{ secrets.STATIC_API_BASE_URL }}
5559
SECURITY_ALERTS_API_BASE_URL: ${{ secrets.SECURITY_ALERTS_API_BASE_URL }}
60+
NFT_API_BASE_URL: ${{ secrets.NFT_API_BASE_URL }}
5661
steps:
5762
- name: Checkout and setup environment
5863
uses: MetaMask/action-checkout-and-setup@v1
5964
with:
6065
is-high-risk-environment: false
66+
node-version-file: ".nvmrc"
67+
cache: "yarn"
6168
- run: yarn --immutable --immutable-cache || yarn install
6269
- run: |
6370
npm rebuild --verbose sharp
@@ -73,6 +80,8 @@ jobs:
7380
uses: MetaMask/action-checkout-and-setup@v1
7481
with:
7582
is-high-risk-environment: false
83+
node-version-file: ".nvmrc"
84+
cache: "yarn"
7685
- run: yarn --immutable --immutable-cache || yarn install
7786
- run: yarn lint
7887
- name: Require clean working directory
@@ -100,11 +109,14 @@ jobs:
100109
TOKEN_API_BASE_URL: ${{ secrets.TOKEN_API_BASE_URL }}
101110
STATIC_API_BASE_URL: ${{ secrets.STATIC_API_BASE_URL }}
102111
SECURITY_ALERTS_API_BASE_URL: ${{ secrets.SECURITY_ALERTS_API_BASE_URL }}
112+
NFT_API_BASE_URL: ${{ secrets.NFT_API_BASE_URL }}
103113
steps:
104114
- name: Checkout and setup environment
105115
uses: MetaMask/action-checkout-and-setup@v1
106116
with:
107117
is-high-risk-environment: false
118+
node-version-file: ".nvmrc"
119+
cache: "yarn"
108120
- run: yarn install --immutable
109121
- name: Build for tests
110122
run: yarn workspace @metamask/solana-wallet-snap build

.github/workflows/create-release-pr.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ on:
44
workflow_dispatch:
55
inputs:
66
base-branch:
7-
description: 'The base branch for git operations and the pull request.'
8-
default: 'main'
7+
description: "The base branch for git operations and the pull request."
8+
default: "main"
99
required: true
1010
release-type:
1111
description: 'A SemVer version diff, i.e. major, minor, or patch. Mutually exclusive with "release-version".'
@@ -27,6 +27,10 @@ jobs:
2727
is-high-risk-environment: false
2828
fetch-depth: 0
2929
ref: ${{ github.event.inputs.base-branch }}
30+
- name: Setup Node.js
31+
uses: actions/setup-node@v4
32+
with:
33+
node-version-file: ".nvmrc"
3034
- uses: MetaMask/action-create-release-pr@v3
3135
env:
3236
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -43,6 +47,7 @@ jobs:
4347
uses: MetaMask/action-checkout-and-setup@v1
4448
with:
4549
is-high-risk-environment: false
50+
node-version-file: ".nvmrc"
4651
- name: Update Snap Version
4752
id: update-release-pr
4853
shell: bash
@@ -62,3 +67,4 @@ jobs:
6267
TOKEN_API_BASE_URL: ${{ secrets.TOKEN_API_BASE_URL }}
6368
STATIC_API_BASE_URL: ${{ secrets.STATIC_API_BASE_URL }}
6469
SECURITY_ALERTS_API_BASE_URL: ${{ secrets.SECURITY_ALERTS_API_BASE_URL }}
70+
NFT_API_BASE_URL: ${{ secrets.NFT_API_BASE_URL }}

.github/workflows/main.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ jobs:
1919
TOKEN_API_BASE_URL: ${{ secrets.TOKEN_API_BASE_URL }}
2020
STATIC_API_BASE_URL: ${{ secrets.STATIC_API_BASE_URL }}
2121
SECURITY_ALERTS_API_BASE_URL: ${{ secrets.SECURITY_ALERTS_API_BASE_URL }}
22+
NFT_API_BASE_URL: ${{ secrets.NFT_API_BASE_URL }}
2223

2324
sonarcloud:
2425
permissions:
@@ -93,3 +94,4 @@ jobs:
9394
TOKEN_API_BASE_URL: ${{ secrets.TOKEN_API_BASE_URL }}
9495
STATIC_API_BASE_URL: ${{ secrets.STATIC_API_BASE_URL }}
9596
SECURITY_ALERTS_API_BASE_URL: ${{ secrets.SECURITY_ALERTS_API_BASE_URL }}
97+
NFT_API_BASE_URL: ${{ secrets.NFT_API_BASE_URL }}

.github/workflows/pr.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ jobs:
3333
TOKEN_API_BASE_URL: ${{ secrets.TOKEN_API_BASE_URL }}
3434
STATIC_API_BASE_URL: ${{ secrets.STATIC_API_BASE_URL }}
3535
SECURITY_ALERTS_API_BASE_URL: ${{ secrets.SECURITY_ALERTS_API_BASE_URL }}
36+
NFT_API_BASE_URL: ${{ secrets.NFT_API_BASE_URL }}
3637

3738
sonarcloud:
3839
permissions:

.github/workflows/publish-release.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ on:
2525
required: true
2626
SECURITY_ALERTS_API_BASE_URL:
2727
required: true
28+
NFT_API_BASE_URL:
29+
required: true
2830
EXPLORER_BASE_URL:
2931
required: true
3032
jobs:
@@ -38,6 +40,10 @@ jobs:
3840
with:
3941
is-high-risk-environment: true
4042
ref: ${{ github.sha }}
43+
- name: Setup Node.js
44+
uses: actions/setup-node@v3
45+
with:
46+
node-version-file: ".nvmrc"
4147
- uses: MetaMask/action-publish-release@v3
4248
env:
4349
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -57,8 +63,11 @@ jobs:
5763
TOKEN_API_BASE_URL: ${{ secrets.TOKEN_API_BASE_URL }}
5864
STATIC_API_BASE_URL: ${{ secrets.STATIC_API_BASE_URL }}
5965
SECURITY_ALERTS_API_BASE_URL: ${{ secrets.SECURITY_ALERTS_API_BASE_URL }}
66+
NFT_API_BASE_URL: ${{ secrets.NFT_API_BASE_URL }}
6067
- name: Upload build artifacts
6168
uses: actions/upload-artifact@v4
69+
- uses: actions/cache@v4
70+
id: restore-build
6271
with:
6372
name: publish-release-artifacts-${{ github.sha }}
6473
include-hidden-files: true

packages/snap/snap.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const environment = {
1616
TOKEN_API_BASE_URL: process.env.TOKEN_API_BASE_URL ?? '',
1717
STATIC_API_BASE_URL: process.env.STATIC_API_BASE_URL ?? '',
1818
SECURITY_ALERTS_API_BASE_URL: process.env.SECURITY_ALERTS_API_BASE_URL ?? '',
19+
NFT_API_BASE_URL: process.env.NFT_API_BASE_URL ?? '',
1920
LOCAL_API_BASE_URL: process.env.LOCAL_API_BASE_URL ?? '',
2021
};
2122

packages/snap/snap.manifest.json

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,32 @@
1616
"registry": "https://registry.npmjs.org/"
1717
}
1818
},
19-
"locales": ["locales/en.json"]
19+
"locales": [
20+
"locales/en.json"
21+
]
2022
},
2123
"initialConnections": {
22-
"https://portfolio.metamask.io": {}
24+
"https://portfolio.metamask.io": {},
25+
"http://localhost:3000": {}
2326
},
2427
"initialPermissions": {
2528
"endowment:rpc": {
2629
"dapps": true,
2730
"snaps": false
2831
},
2932
"endowment:keyring": {
30-
"allowedOrigins": ["https://portfolio.metamask.io"]
33+
"allowedOrigins": [
34+
"https://portfolio.metamask.io",
35+
"http://localhost:3000"
36+
]
3137
},
3238
"snap_getBip32Entropy": [
3339
{
34-
"path": ["m", "44'", "501'"],
40+
"path": [
41+
"m",
42+
"44'",
43+
"501'"
44+
],
3545
"curve": "ed25519"
3646
}
3747
],
@@ -92,4 +102,4 @@
92102
},
93103
"platformVersion": "7.1.0",
94104
"manifestVersion": "0.1"
95-
}
105+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/* eslint-disable @typescript-eslint/naming-convention */
2+
/* eslint-disable no-restricted-globals */
3+
import { assert } from '@metamask/superstruct';
4+
import { Duration } from '@metamask/utils';
5+
6+
import type { ICache } from '../../caching/ICache';
7+
import type { Serializable } from '../../serialization/types';
8+
import type { ConfigProvider } from '../../services/config';
9+
import { buildUrl } from '../../utils/buildUrl';
10+
import type { ILogger } from '../../utils/logger';
11+
import logger from '../../utils/logger';
12+
import { UrlStruct } from '../../validation/structs';
13+
import { MOCK_NFTS_LIST_RESPONSE_MAPPED } from './mocks/mockNftsListResponseMapped';
14+
import { mapGetNftMetadataResponse } from './utils/mapGetNftMetadataResponse';
15+
16+
export class NftApiClient {
17+
readonly #fetch: typeof globalThis.fetch;
18+
19+
readonly #logger: ILogger;
20+
21+
readonly #baseUrl: string;
22+
23+
readonly #cache: ICache<Serializable>;
24+
25+
public static readonly cacheTtlsMilliseconds = {
26+
fiatExchangeRates: Duration.Minute,
27+
spotPrices: Duration.Minute,
28+
historicalPrices: Duration.Minute,
29+
};
30+
31+
constructor(
32+
configProvider: ConfigProvider,
33+
_cache: ICache<Serializable>,
34+
_fetch: typeof globalThis.fetch = globalThis.fetch,
35+
_logger: ILogger = logger,
36+
) {
37+
const { baseUrl } = configProvider.get().nftApi;
38+
39+
assert(baseUrl, UrlStruct);
40+
41+
this.#fetch = _fetch;
42+
this.#logger = _logger;
43+
this.#baseUrl = baseUrl;
44+
45+
this.#cache = _cache;
46+
}
47+
48+
async listAddressSolanaNfts(address: string) {
49+
// let allItems: Balance[] = [];
50+
// let currentCursor: string | undefined;
51+
52+
// do {
53+
// const url = buildUrl({
54+
// baseUrl: this.#baseUrl,
55+
// path: `/users/${address}/solana-tokens`,
56+
// queryParams: currentCursor ? { cursor: currentCursor } : undefined,
57+
// });
58+
// const response = await this.#fetch(url, {
59+
// headers: {
60+
// 'Content-Type': 'application/json',
61+
// accept: 'application/json',
62+
// version: '1',
63+
// },
64+
// });
65+
// const data = await response.json();
66+
67+
// const mappedData = mapListAddressSolanaNftsResponse(data);
68+
69+
// console.log('FOUND THESE NFTS');
70+
// console.log(mappedData.items.length);
71+
72+
// allItems = [...allItems, ...mappedData.items];
73+
// currentCursor = mappedData.cursor ?? undefined;
74+
// } while (currentCursor);
75+
76+
// return allItems;
77+
78+
return MOCK_NFTS_LIST_RESPONSE_MAPPED.items;
79+
}
80+
81+
async getNftMetadata(tokenAddress: string) {
82+
try {
83+
const url = buildUrl({
84+
baseUrl: this.#baseUrl,
85+
path: `/nfts/contracts/solana/${tokenAddress}/1`,
86+
});
87+
const response = await this.#fetch(url);
88+
const data = await response.json();
89+
90+
const mappedData = mapGetNftMetadataResponse(data);
91+
92+
return mappedData;
93+
} catch (error) {
94+
return null;
95+
}
96+
}
97+
98+
async getNftsMetadata(tokenAddresses: string[]) {
99+
const nftsMetadata = await Promise.all(
100+
tokenAddresses.map(async (tokenAddress) =>
101+
this.getNftMetadata(tokenAddress),
102+
),
103+
);
104+
105+
return nftsMetadata;
106+
}
107+
}

0 commit comments

Comments
 (0)