@eluvio/elv-client-js 3.2.4 → 3.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/ElvClient-min.js +15 -11
  2. package/dist/ElvClient-node-min.js +17 -13
  3. package/dist/ElvFrameClient-min.js +12 -8
  4. package/dist/ElvPermissionsClient-min.js +13 -9
  5. package/dist/ElvWalletClient-min.js +58 -0
  6. package/dist/ElvWalletClient-node-min.js +78 -0
  7. package/dist/src/AuthorizationClient.js +2248 -1990
  8. package/dist/src/ContentObjectVerification.js +164 -173
  9. package/dist/src/Crypto.js +376 -324
  10. package/dist/src/ElvClient.js +1198 -1019
  11. package/dist/src/ElvWallet.js +119 -95
  12. package/dist/src/EthClient.js +1040 -896
  13. package/dist/src/FrameClient.js +331 -300
  14. package/dist/src/HttpClient.js +153 -147
  15. package/dist/src/Id.js +1 -3
  16. package/dist/src/PermissionsClient.js +1294 -1168
  17. package/dist/src/RemoteSigner.js +263 -211
  18. package/dist/src/UserProfileClient.js +1164 -1023
  19. package/dist/src/Utils.js +229 -184
  20. package/dist/src/client/ABRPublishing.js +895 -858
  21. package/dist/src/client/AccessGroups.js +1102 -959
  22. package/dist/src/client/ContentAccess.js +3724 -3431
  23. package/dist/src/client/ContentManagement.js +2252 -2068
  24. package/dist/src/client/Contracts.js +647 -563
  25. package/dist/src/client/Files.js +1886 -1757
  26. package/dist/src/client/NFT.js +126 -112
  27. package/dist/src/client/NTP.js +478 -422
  28. package/dist/src/index.js +11 -0
  29. package/dist/src/marketplaceClient/ClientMethods.js +1918 -0
  30. package/dist/src/marketplaceClient/Configuration.js +29 -0
  31. package/dist/src/marketplaceClient/Utils.js +304 -0
  32. package/dist/src/marketplaceClient/index.js +1553 -0
  33. package/dist/src/walletClient/ClientMethods.js +2080 -0
  34. package/dist/src/walletClient/Configuration.js +29 -0
  35. package/dist/src/walletClient/Utils.js +304 -0
  36. package/dist/src/walletClient/index.js +1770 -0
  37. package/package.json +8 -4
  38. package/src/ElvClient.js +5 -2
  39. package/src/Utils.js +22 -4
  40. package/src/index.js +7 -0
  41. package/src/walletClient/ClientMethods.js +1111 -0
  42. package/src/walletClient/Configuration.js +40 -0
  43. package/src/walletClient/README.md +191 -0
  44. package/src/walletClient/Utils.js +234 -0
  45. package/src/walletClient/index.js +977 -0
  46. package/testScripts/TestMarketplaceClient.js +25 -0
@@ -0,0 +1,40 @@
1
+ let WalletConfiguration = {
2
+ demo: {
3
+ configUrl: "https://demov3.net955210.contentfabric.io/config",
4
+ staging: {
5
+ siteId: "iq__2gkNh8CCZqFFnoRpEUmz7P3PaBQG",
6
+ purchaseMode: "develop",
7
+ appUrl: "https://core.test.contentfabric.io/wallet-demo"
8
+ }
9
+ },
10
+ main: {
11
+ configUrl: "https://main.net955305.contentfabric.io/config",
12
+ staging: {
13
+ siteId: "iq__inauxD1KLyKWPHargCWjdCh2ayr",
14
+ purchaseMode: "production",
15
+ appUrl: "https://core.test.contentfabric.io/wallet"
16
+ },
17
+ production: {
18
+ siteId: "iq__suqRJUt2vmXsyiWS5ZaSGwtFU9R",
19
+ purchaseMode: "production",
20
+ appUrl: "https://wallet.contentfabric.io"
21
+ }
22
+ },
23
+ __MARKETPLACE_ORDER: [
24
+ "dolly-marketplace",
25
+ "oc-marketplace",
26
+ "maskverse-marketplace",
27
+ "emp-marketplace",
28
+ "microsoft",
29
+ "indieflix-marketplace",
30
+ "angels-airwaves-marketplace"
31
+ ]
32
+ };
33
+
34
+ // No production environment on demo
35
+ WalletConfiguration.demo.production = WalletConfiguration.demo.staging;
36
+
37
+ // Allow demo to be referred to as demov3
38
+ WalletConfiguration.demov3 = WalletConfiguration.demo;
39
+
40
+ module.exports = WalletConfiguration;
@@ -0,0 +1,191 @@
1
+ # Eluvio Wallet Client
2
+
3
+ This is a standalone client for using Eluvio Media Wallet functionality
4
+
5
+ For information about the embedded wallet app frame client, see [here](https://eluv-io.github.io/elv-media-wallet/ElvWalletFrameClient.html)
6
+
7
+ ## Initializing the Client
8
+
9
+ Import the client using the following path:
10
+
11
+ ```javascript
12
+ import {ElvWalletClient} from "@eluvio/elv-client-js/src/walletClient/index";
13
+ ```
14
+
15
+ Initializing the client is as simple as calling the static Initialize method with the network (main or demo) and the mode (production or staging) the client should operate upon, as well as (optionally) the marketplace to associate the client with.
16
+
17
+ ```javascript
18
+ await ElvWalletClient.Initialize({
19
+ network: "main | demo",
20
+ mode: "production | staging",
21
+ marketplaceParams: {
22
+ tenantSlug,
23
+ marketplaceSlug
24
+ }
25
+ })
26
+ ```
27
+
28
+ ## Referencing Marketplaces
29
+
30
+ Many methods, including the Initialize method, take a parameter called `marketplaceParams`. This parameter should be specified in one of the following formats:
31
+
32
+ Specify the tenant slug and marketplace slug corresponding to your marketplace.
33
+ ```javascript
34
+ {
35
+ tenantSlug: "<tenant-slug>",
36
+ marketplaceSlug: "<marketplace-slug>"
37
+ }
38
+ ```
39
+
40
+ Specify the object ID of the marketplace
41
+ ```javascript
42
+ { marketplaceId: "iq__abc123" }
43
+ ```
44
+
45
+ Specify a version hash of the marketplace
46
+ ```javascript
47
+ { marketplaceHash: "hq__abc123" }
48
+ ```
49
+
50
+ ## Handling Login
51
+
52
+ The wallet client make it simple to log in with the Eluvio Media Wallet. However, if you are also using the frame client to embed the wallet application, extra care must be taken to ensure the login flow is handled correctly and the wallet client and frame have parity in their authorization states.
53
+
54
+ <div id="wallet-client" />
55
+
56
+ ### Wallet Client
57
+
58
+ ##### Redirect Flow
59
+ ```javascript
60
+ walletClient.LogIn({
61
+ method: "redirect",
62
+ callbackUrl: <https://your-domain.com/callback>
63
+ });
64
+ ```
65
+
66
+ Upon login, the browser will return to the callbackUrl with the authorization token as a url parameter (`elvToken`). **Initializing the client will automatically pull the token out of the URL parameters and log in.**
67
+
68
+ If you don't want to initialize the client in the callback URL, you can save this token and later set it in the client using the `Authenticate` method.
69
+
70
+ ```javascript
71
+ const searchParams = new URLSearchParams(window.location.search);
72
+ const clientAuthToken = searchParams.get("elvToken");
73
+
74
+ ...
75
+
76
+ walletClient.Authorize({token: clientAuthToken});
77
+
78
+ ```
79
+
80
+ ##### Popup Flow
81
+ ```javascript
82
+ try {
83
+ await walletClient.LogIn({
84
+ method: "popup"
85
+ })
86
+ } catch(error) {
87
+ // User closed popup
88
+ }
89
+ ```
90
+
91
+ ##### Additional Options
92
+
93
+ The `clear` parameter in the LogIn method will ensure that the user does the full log in flow, even if they are already logged in to the Eluvio Media Wallet application. This is important to specify if the user has logged out from your application, as they will otherwise still be logged in to the Media Wallet and the login flow will simply authorize them as the same user.
94
+
95
+ The `provider` parameter will automatically proceed to log in with the specified provider instead of presenting the usual login screen and waiting for the user to choose. For example, you can specify that the login flow should automatically prompt for Metamask login or proceed to the OAuth login flow. For the OAuth flow, the `mode` parameter can be used to proceed to the `login` flow or the `create` account flow.
96
+
97
+ ##### Saving Authorization
98
+
99
+ By default, the client will save the authorization token in localstorage, if available. If you prefer to handle storing the authorization token differently, you can specify that the client should not save the token when initializing the client:
100
+
101
+ ```javascript
102
+ const walletClient = ElvWalletClient.Initialize({
103
+ ...,
104
+ storeAuthToken: false
105
+ })
106
+ ```
107
+
108
+ The authorization token can be retrieved using `walletClient.ClientAuthToken()`, which can then be used later in the `Authenticate` method.
109
+
110
+
111
+ ### Frame Client
112
+
113
+ If you only want to embed the Eluvio Media Wallet application without using the Wallet Client and you don't want to implement a custom login screen, no special effort is needed. The wallet app UI will handle login natively.
114
+
115
+ You can use event listeners with the frame client to detect when the user logs in or out:
116
+
117
+ ```javascript
118
+ frameClient.AddEventListener(frameClient.EVENTS.LOG_IN, ({address}) => { ... });
119
+
120
+ frameClient.AddEventListener(frameClient.EVENTS.LOG_OUT, () => { ... });
121
+ ```
122
+
123
+
124
+ ### Custom Login UI For Embedded Wallet App / Using Both Wallet Client and Frame Client
125
+
126
+ If you want to implement a custom login screen for the embedded wallet application, or if you want to use both the wallet and frame clients in the same application, some care must be taken to ensure the login flow is handled correctly and the wallet client and frame have parity in their authorization states.
127
+
128
+ First, **login should be handled by the Wallet Client only**. The wallet client does not have to deal with the limitations of iframes, and the wallet client is allowed access to the user's authorization token, which can be passed to the frame client to authorize the embedded application.
129
+
130
+ Authorization can be passed to the embedded frame, but **cannot be retrieved** from it. Because of this, logging in via the embedded application would mean the user is logged in in the frame, but the wallet client in the containing application has no access to that user's authorization.
131
+
132
+ Here's how to handle this case:
133
+
134
+ ##### Perform Login Using the Wallet Client
135
+
136
+ See the [Wallet Client](#wallet-client) login section for details about how to handle the login flow with the wallet client.
137
+
138
+ ##### Capture Login from the Frame
139
+
140
+ When initializing the frame client, specify the `captureLogin` option. This will cause login prompts (e.g. user clicks log in, user tries to perform/access something that requires login, etc.) to emit an event instead of presenting the login form in the frame.
141
+
142
+ ```javascript
143
+ const frameClient = await ElvWalletFrameClient.InitializeFrame({
144
+ ...,
145
+ captureLogin: true
146
+ })
147
+ ```
148
+
149
+ You can then listen for that event to present your own login screen, or simply trigger the login flow with the Wallet Client.
150
+
151
+ ```javascript
152
+ frameClient.AddEventListener(
153
+ frameClient.EVENTS.LOG_IN_REQUESTED,
154
+ () => {
155
+ // Show your login screen or log in with wallet client
156
+ // ShowLogin()
157
+ // walletClient.LogIn({...});
158
+ }
159
+ )
160
+ ```
161
+
162
+ ##### Pass Authorization Token to Frame Client
163
+
164
+ When the login flow has completed with the wallet client, pass the authorization token to the embedded application:
165
+
166
+ ```javascript
167
+ if(walletClient.ClientAuthToken()) {
168
+ await frameClient.LogIn({clientAuthToken: walletClient.ClientAuthToken()})
169
+ }
170
+ ```
171
+
172
+ ##### Handle Log Out in Both Clients
173
+
174
+ After the user is logged in with the frame client, you can present an option to log out. Be sure this logs out both the wallet client and the frame client:
175
+
176
+ ```javascript
177
+ function LogOutFromMyApp() {
178
+ walletClient.LogOut();
179
+ frameClient.LogOut();
180
+ }
181
+ ```
182
+
183
+ Additionally, the user may log out from the embedded application. Use an event listener in the frame client to handle this and log out from the wallet client:
184
+
185
+ ```javascript
186
+ frameClient.AddEventListener(
187
+ frameClient.EVENTS.LOG_OUT,
188
+ () => LogOutFromMyApp()
189
+ );
190
+ ```
191
+
@@ -0,0 +1,234 @@
1
+ const Utils = require("../Utils");
2
+
3
+ const RarityToPercentage = (rarity) => {
4
+ if(!rarity) {
5
+ return "";
6
+ }
7
+
8
+ rarity = rarity.toString();
9
+
10
+ if(!rarity.includes("/")) {
11
+ return rarity;
12
+ }
13
+
14
+ const [ numerator, denominator ] = rarity.split("/");
15
+ let percentage = 100 * parseInt(numerator) / parseInt(denominator);
16
+
17
+ if(percentage < 1) {
18
+ percentage = percentage.toFixed(2);
19
+ } else {
20
+ percentage = percentage.toFixed(1).toString().replace(".0", "");
21
+ }
22
+
23
+ return percentage;
24
+ };
25
+
26
+ // Format NFT or listing result into consistent format
27
+ const FormatNFTDetails = function(entry) {
28
+ const isListing = !!entry.id;
29
+
30
+ const metadata = (isListing ? entry.nft : entry.meta) || {};
31
+ const info = (isListing ? entry.info : entry);
32
+
33
+ let details = {
34
+ USDCAccepted: !!(entry.accepts || []).find(entry => entry.type === "sol"),
35
+ USDCOnly: ((entry.accepts || []).find(entry => entry.type === "sol") || {}).preferred,
36
+ TenantId: entry.tenant || entry.tenant_id,
37
+ ContractAddr: info.contract_addr,
38
+ ContractId: `ictr${Utils.AddressToHash(info.contract_addr)}`,
39
+ ContractName: info.contract_name,
40
+ Cap: info.cap,
41
+ TokenIdStr: info.token_id_str,
42
+ TokenUri: info.token_uri,
43
+ TokenOrdinal: info.ordinal,
44
+ TokenHold: info.hold,
45
+ TokenHoldDate: info.hold ? new Date(parseInt(info.hold) * 1000) : undefined,
46
+ TokenOwner: info.token_owner ? Utils.FormatAddress(info.token_owner) : "",
47
+ VersionHash: (info.token_uri || "").split("/").find(s => (s || "").startsWith("hq__")),
48
+ };
49
+
50
+ if(isListing) {
51
+ details = {
52
+ ...details,
53
+ // Listing specific fields
54
+ ListingId: entry.id,
55
+ CreatedAt: entry.created * 1000,
56
+ UpdatedAt: entry.updated * 1000,
57
+ CheckoutLockedUntil: entry.checkout ? entry.checkout * 1000 : undefined,
58
+ SellerAddress: Utils.FormatAddress(entry.seller),
59
+ Price: entry.price,
60
+ Fee: entry.fee
61
+ };
62
+ }
63
+
64
+ return {
65
+ metadata,
66
+ details
67
+ };
68
+ };
69
+
70
+ exports.FormatNFTDetails = FormatNFTDetails;
71
+
72
+
73
+ const FormatNFTMetadata = function(nft) {
74
+ nft.formatted = true;
75
+
76
+ // Surface relevant details to top level
77
+ nft.contractAddress = nft.details.ContractAddr;
78
+ nft.contractId = nft.details.ContractId;
79
+ nft.tokenId = nft.details.TokenIdStr;
80
+ nft.name = nft.metadata.display_name;
81
+
82
+ if(nft.details.ListingId) {
83
+ nft.listingId = nft.details.ListingId;
84
+ }
85
+
86
+ // Format traits
87
+ const FILTERED_ATTRIBUTES = ["Content Fabric Hash", "Creator", "Total Minted Supply"];
88
+ nft.metadata.attributes = (nft.metadata.attributes || [])
89
+ .filter(attribute => attribute && !FILTERED_ATTRIBUTES.includes(attribute.trait_type))
90
+ .map(trait => ({...trait, name: trait.trait_type, rarity_percent: RarityToPercentage(trait.rarity)}));
91
+
92
+ // Generate embed URLs for additional media
93
+ if(nft.metadata.additional_media) {
94
+ nft.metadata.additional_media = nft.metadata.additional_media.map(media => {
95
+ try {
96
+ // Generate embed URLs for additional media
97
+ const mediaType = (media.media_type || "").toLowerCase();
98
+
99
+ if(mediaType === "image") {
100
+ return {
101
+ ...media,
102
+ embed_url: media.media_file.url
103
+ };
104
+ }
105
+
106
+ let embedUrl = new URL("https://embed.v3.contentfabric.io");
107
+ embedUrl.searchParams.set("p", "");
108
+ embedUrl.searchParams.set("net", rootStore.network === "demo" ? "demo" : "main");
109
+ embedUrl.searchParams.set("ath", rootStore.authToken);
110
+
111
+ if(mediaType === "video") {
112
+ embedUrl.searchParams.set("vid", LinkTargetHash(media.media_link));
113
+ embedUrl.searchParams.set("ct", "h");
114
+ embedUrl.searchParams.set("ap", "");
115
+ } else if(mediaType === "ebook") {
116
+ embedUrl.searchParams.set("type", "ebook");
117
+ embedUrl.searchParams.set("vid", media.media_file["."].container);
118
+ embedUrl.searchParams.set("murl", btoa(media.media_file.url));
119
+ }
120
+
121
+ return {
122
+ ...media,
123
+ embed_url: embedUrl.toString()
124
+ };
125
+ } catch(error) {
126
+ return media;
127
+ }
128
+ });
129
+ }
130
+
131
+ // Generate embed URLs for pack opening animations
132
+ ["open_animation", "open_animation__mobile", "reveal_animation", "reveal_animation_mobile"].forEach(key => {
133
+ try {
134
+ if(nft.metadata.pack_options && nft.metadata.pack_options[key]) {
135
+ let embedUrl = new URL("https://embed.v3.contentfabric.io");
136
+ embedUrl.searchParams.set("p", "");
137
+ embedUrl.searchParams.set("net", rootStore.network === "demo" ? "demo" : "main");
138
+ embedUrl.searchParams.set("ath", rootStore.authToken || rootStore.staticToken);
139
+ embedUrl.searchParams.set("vid", LinkTargetHash(nft.metadata.pack_options[key]));
140
+ embedUrl.searchParams.set("ap", "");
141
+
142
+ if(!key.startsWith("reveal")) {
143
+ embedUrl.searchParams.set("m", "");
144
+ embedUrl.searchParams.set("lp", "");
145
+ }
146
+
147
+ nft.metadata.pack_options[`${key}_embed_url`] = embedUrl.toString();
148
+ }
149
+ // eslint-disable-next-line no-empty
150
+ } catch(error) {}
151
+ });
152
+
153
+ return nft;
154
+ };
155
+
156
+ exports.FormatNFTMetadata = FormatNFTMetadata;
157
+
158
+ exports.FormatNFT = function (item) {
159
+ return FormatNFTMetadata(FormatNFTDetails(item));
160
+ };
161
+
162
+
163
+ exports.LinkTargetHash = function(link) {
164
+ if(!link) { return; }
165
+
166
+ if(link["."] && link["."].source) {
167
+ return link["."].source;
168
+ }
169
+
170
+ if(link["/"] && link["/"].startsWith("/qfab/")) {
171
+ return link["/"].split("/").find(segment => segment.startsWith("hq__"));
172
+ }
173
+
174
+ if(link["."] && link["."].container) {
175
+ return link["."].container;
176
+ }
177
+ };
178
+
179
+ // https://stackoverflow.com/questions/4068373/center-a-popup-window-on-screen
180
+ const Popup = ({url, title, w, h}) => {
181
+ // Fixes dual-screen position
182
+ const dualScreenLeft = window.screenLeft || window.screenX;
183
+ const dualScreenTop = window.screenTop || window.screenY;
184
+
185
+ const width = window.innerWidth || document.documentElement.clientWidth || screen.width;
186
+ const height = window.innerHeight || document.documentElement.clientHeight || screen.height;
187
+
188
+ const systemZoom = width / window.screen.availWidth;
189
+ const left = (width - w) / 2 / systemZoom + dualScreenLeft;
190
+ const top = (height - h) / 2 / systemZoom + dualScreenTop;
191
+ const newWindow = window.open(url, title,
192
+ `
193
+ width=${w / systemZoom},
194
+ height=${h / systemZoom},
195
+ top=${top},
196
+ left=${left}
197
+ `
198
+ );
199
+
200
+ if(window.focus) newWindow.focus();
201
+
202
+ return newWindow;
203
+ };
204
+
205
+ exports.ActionPopup = async ({mode="tab", url, onMessage, onCancel}) => {
206
+ await new Promise(resolve => {
207
+ const newWindow = mode === "popup" ?
208
+ Popup({url, title: "Eluvio Media Wallet", w: 500, h: 850}) :
209
+ window.open(url);
210
+
211
+ const closeCheck = setInterval(async () => {
212
+ if(newWindow.closed) {
213
+ clearInterval(closeCheck);
214
+
215
+ if(onCancel) {
216
+ await onCancel();
217
+ }
218
+
219
+ resolve();
220
+ }
221
+ }, 1000);
222
+
223
+ window.addEventListener("message", async event => {
224
+ await onMessage(
225
+ event,
226
+ () => {
227
+ clearInterval(closeCheck);
228
+ newWindow.close();
229
+ resolve();
230
+ }
231
+ );
232
+ });
233
+ });
234
+ };