diff --git a/CHANGELOG.md b/CHANGELOG.md
index 31ec8d42..71b76caa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,10 @@
> Description
-Minor release for off chain token attestations
+Feature release including multi-hook support, improved UX for off chain token holders and general improvements.
### Upgrade Steps
-- Update NPM package to version 3.1.1
+- Update NPM package to version 3.2.0
### Breaking Changes
@@ -12,19 +12,26 @@ Minor release for off chain token attestations
### New Features
-- Upgrade attestation lib to support validation of new EAS off-chain attestation version
-- Prevent delete of signedToken property on token object
-- Change single token auth to use tokenId instead of sending the entire token object in URL
-- Add labels to default ticket schema
+- Extended the configuration options for 'active' mode ui text
+- Off Chain Token Issuer UX improvements to notify the end user when the page will re-direct
+- Re-direct view state UX improvements for 'active' mode
+- Re-direct accept / deny options given to end user (to cancel the loading of tokens)
+- Multi-Hook support added
+- Added selected token issuer keys to 'tokens-selected' event hook
+- Explicitly include ethers library availabilty via Token Negotiator library interface `client.externalUtils.evm.ethers`
+- Aligned on chain authentication with off chain user interface (for single token authentication on the client side). Multi token on chain authentication is not yet supported (via the current library features).
### Bug Fixes
-[none]
+- TS interface custom view
+- Single token off chain authentication incompatibility interface fix
+- EAS authentication fixed via attestation.id updated dependency @tokenscript/attestation to version "0.7.0-rc.2"
-### Performance Improvements
+### Performance / Quality Improvements
-[none]
+- Reduced the duplication of variable definitions (moved to a common constants file).
+- Increased unit test coverage
**Full Change log**:
-https://github.com/TokenScript/token-negotiator/compare/v3.1.0....v3.1.1
+https://github.com/TokenScript/token-negotiator/compare/v3.1.1....v3.2.0
diff --git a/README.md b/README.md
index 86b9c781..edec7f96 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# `token-negotiator`
-The Token Negotiator provides the client gateway to connect user digital tokens from on or off chain sources, enabling developers to create bespoke tokenised web experiences.
+Token Negotiator is an open source technology that enables you build web experiences for users around the tokens, collectibles, coins and attestations they hold. Enabled across EVM, Solana, Flow, Chiliz and EOS (Ultra). Create logic flows to enable personalized web experiences.
## Usage
@@ -29,8 +29,8 @@ const negotiator = new Client({
negotiator.negotiate();
-negotiator.on("tokens-selected", (tokens) => {
- console.log('owner tokens found: ', tokens);
+negotiator.on("tokens-selected", ({ selectedTokens, selectedIssuerKeys }) => {
+ console.log('user selected tokens: ', selectedTokens);
});
```
diff --git a/index.html b/index.html
index 21972878..e7f29069 100644
--- a/index.html
+++ b/index.html
@@ -20,108 +20,115 @@
window.negotiator = new negotiator.Client({
type: "active",
issuers: [
- {
- onChain: true,
- fungible: true,
- chain: "eth",
- blockchain: "evm",
- collectionID: "socios",
- contract: "0x3506424F91fD33084466F402d5D97f05F8e3b4AF",
- oAuth2options: {
- consumerKey: "YOUR_CONSUMER_KEY",
- partnerTag: "YOUR_PARTNER_TAG",
- endpoints: {
- redirectURI: {
- path: "http://localhost:5000/user-login-callback",
- params: {}
- },
- userBalance: {
- path: 'http://localhost:5000/user-balance',
- params: {}
- },
- userNfts: {
- path: 'http://localhost:5000/user-nfts',
- params: {}
- },
- userLogout: {
- path: 'http://localhost:5000/user-logout',
- params: {}
- },
- },
- },
- },
- {
- hideToggle: true,
- noTokenMsg:
- "
If you hold a Devcon ticket, please visit your magicLink email again and open the link in this browser.
You may reload this page after and try again.
",
- onChain: false,
- tokenName: "devcon6",
- collectionID: "devcon6",
- title: "Devcon VI Ticket Attestation",
- image: "https://devcon-vi.attest.tickets/img/nft_bg.png",
- tokenOrigin: "https://devcon-vi.attest.tickets/outlet.html",
- attestationOrigin: "https://test.attestation.id/",
- tokenUrlName: "ticket",
- cryptoVerify: "https://form.smarttokenlabs.com",
- tokenSecretName: "secret",
- tokenIdName: "mail",
- unsignedTokenDataName: "ticket",
- whitelistDialogRenderer: (permissionTxt, acceptBtn, denyBtn) => {
- return `
-
-
-
-
-
-
${permissionTxt}
- ${acceptBtn}
- ${denyBtn}
-
-
- `;
- },
- signedTokenWhitelist: [
- "https://devcon-vi.attest.tickets/",
- "https://dev.loc",
- "https://devconnect.loc/",
- "https://devconnect.antopolbus.rv.ua/",
- "https://perks.antopolbus.rv.ua/",
- "https://stage-perks.smarttokenlabs.com/",
- "http://localhost:5000/",
+ // {
+ // onChain: true,
+ // fungible: true,
+ // chain: "eth",
+ // blockchain: "evm",
+ // collectionID: "socios",
+ // contract: "0x3506424F91fD33084466F402d5D97f05F8e3b4AF",
+ // oAuth2options: {
+ // consumerKey: "YOUR_CONSUMER_KEY",
+ // partnerTag: "YOUR_PARTNER_TAG",
+ // endpoints: {
+ // redirectURI: {
+ // path: "http://localhost:5000/user-login-callback",
+ // params: {}
+ // },
+ // userBalance: {
+ // path: 'http://localhost:5000/user-balance',
+ // params: {}
+ // },
+ // userNfts: {
+ // path: 'http://localhost:5000/user-nfts',
+ // params: {}
+ // },
+ // userLogout: {
+ // path: 'http://localhost:5000/user-logout',
+ // params: {}
+ // },
+ // },
+ // },
+ // },
+ // {
+ // hideToggle: true,
+ // noTokenMsg:
+ // "If you hold a Devcon ticket, please visit your magicLink email again and open the link in this browser.
You may reload this page after and try again.
",
+ // onChain: false,
+ // tokenName: "devcon6",
+ // collectionID: "devcon6",
+ // title: "Devcon VI Ticket Attestation",
+ // image: "https://devcon-vi.attest.tickets/img/nft_bg.png",
+ // tokenOrigin: "https://devcon-vi.attest.tickets/outlet.html",
+ // attestationOrigin: "https://test.attestation.id/",
+ // tokenUrlName: "ticket",
+ // cryptoVerify: "https://form.smarttokenlabs.com",
+ // tokenSecretName: "secret",
+ // tokenIdName: "mail",
+ // unsignedTokenDataName: "ticket",
+ // whitelistDialogRenderer: (permissionTxt, acceptBtn, denyBtn) => {
+ // return `
+ //
+ //
+ //
+ //
+ //
+ //
${permissionTxt}
+ // ${acceptBtn}
+ // ${denyBtn}
+ //
+ //
+ // `;
+ // },
+ // signedTokenWhitelist: [
+ // "https://devcon-vi.attest.tickets/",
+ // "https://dev.loc",
+ // "https://devconnect.loc/",
+ // "https://devconnect.antopolbus.rv.ua/",
+ // "https://perks.antopolbus.rv.ua/",
+ // "https://stage-perks.smarttokenlabs.com/",
+ // "http://localhost:5000/",
- "https://hashkey.smarttokenlabs.com/",
- "https://fayre-devcon.webflow.io/",
- "https://fenbushi-devcon.webflow.io/",
- "https://metasearch-devcon.webflow.io/",
- "https://conspicuis-devcon.webflow.io/",
- "https://devcon-vi.brandextender.io/",
- "https://www.edcon.io/",
- ],
- itemStorageKey: "devconnectTokens",
- base64senderPublicKeys: {
- 4: "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAGJAHCiHbrCNAY9fAMdom4dGD6v/KkTIgRCkwLCjXFTkXWGrCEXHaZ8kWwdqlu0oYCrNQ2vdlqOl0s26/LzO8A==|MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE-N-jGYo1OuO77XItd3zT-oIhZEVC44uqOhtJkmBsBNDTp3Seu_vmuLB-b4whNeFNBuJTsT7AHUiUe3JOwCcegA==",
- },
- base64attestorPubKey:
- "MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////////////////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEEeb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQNkFBAgEBA0IABL+y43T1OJFScEep69/yTqpqnV/jzONz9Sp4TEHyAJ7IPN9+GHweCX1hT4OFxt152sBN3jJc1s0Ymzd8pNGZNoQ=",
- },
- {
- collectionID: "devcon",
- onChain: false,
- title: "Devcon",
- tokenIdName: "id",
- image:
- "https://raw.githubusercontent.com/TokenScript/token-negotiator/main/mock-images/devcon.svg",
- tokenOrigin: "http://localhost:3000/",
- attestationOrigin: "https://test.attestation.id/",
- base64senderPublicKeys: {
- 6: "MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////////////////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEEeb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQNkFBAgEBA0IABGMxHraqggr2keTXszIcchTjYjH5WXpDaBOYgXva82mKcGnKgGRORXSmcjWN2suUCMkLQj3UNlZCFWF10wIrrlw=",
- 55: "MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////////////////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEEeb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQNkFBAgEBA0IABGMxHraqggr2keTXszIcchTjYjH5WXpDaBOYgXva82mKcGnKgGRORXSmcjWN2suUCMkLQj3UNlZCFWF10wIrrlw=",
- },
- base64attestorPubKey:
- "MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////////////////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEEeb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQNkFBAgEBA0IABL+y43T1OJFScEep69/yTqpqnV/jzONz9Sp4TEHyAJ7IPN9+GHweCX1hT4OFxt152sBN3jJc1s0Ymzd8pNGZNoQ=",
- ticketIssuersUrlWebsitePrivateKey:
- "MIICSwIBADCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////////////////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEEeb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQNkFBAgEBBIIBVTCCAVECAQEEIM/T+SzcXcdtcNIqo6ck0nJTYzKL5ywYBFNSpI7R8AuBoIHjMIHgAgEBMCwGByqGSM49AQECIQD////////////////////////////////////+///8LzBEBCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcEQQR5vmZ++dy7rFWgYpXOhwsHApv82y3OKNlZ8oFbFvgXmEg62ncmo8RlXaT7/A4RCKj9F7RIpoVUGZxH0I/7ENS4AiEA/////////////////////rqu3OavSKA7v9JejNA2QUECAQGhRANCAARjMR62qoIK9pHk17MyHHIU42Ix+Vl6Q2gTmIF72vNpinBpyoBkTkV0pnI1jdrLlAjJC0I91DZWQhVhddMCK65c",
- },
+ // "https://hashkey.smarttokenlabs.com/",
+ // "https://fayre-devcon.webflow.io/",
+ // "https://fenbushi-devcon.webflow.io/",
+ // "https://metasearch-devcon.webflow.io/",
+ // "https://conspicuis-devcon.webflow.io/",
+ // "https://devcon-vi.brandextender.io/",
+ // "https://www.edcon.io/",
+ // ],
+ // itemStorageKey: "devconnectTokens",
+ // base64senderPublicKeys: {
+ // 4: "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAGJAHCiHbrCNAY9fAMdom4dGD6v/KkTIgRCkwLCjXFTkXWGrCEXHaZ8kWwdqlu0oYCrNQ2vdlqOl0s26/LzO8A==|MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE-N-jGYo1OuO77XItd3zT-oIhZEVC44uqOhtJkmBsBNDTp3Seu_vmuLB-b4whNeFNBuJTsT7AHUiUe3JOwCcegA==",
+ // },
+ // base64attestorPubKey:
+ // "MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////////////////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEEeb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQNkFBAgEBA0IABL+y43T1OJFScEep69/yTqpqnV/jzONz9Sp4TEHyAJ7IPN9+GHweCX1hT4OFxt152sBN3jJc1s0Ymzd8pNGZNoQ=",
+ // },
+ // {
+ // collectionID: "devcon",
+ // onChain: false,
+ // title: "Devcon",
+ // tokenIdName: "id",
+ // image:
+ // "https://raw.githubusercontent.com/TokenScript/token-negotiator/main/mock-images/devcon.svg",
+ // tokenOrigin: "http://localhost:3002/",
+ // attestationOrigin: "https://test.attestation.id/",
+ // base64senderPublicKeys: {
+ // 6: "MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////////////////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEEeb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQNkFBAgEBA0IABGMxHraqggr2keTXszIcchTjYjH5WXpDaBOYgXva82mKcGnKgGRORXSmcjWN2suUCMkLQj3UNlZCFWF10wIrrlw=",
+ // 55: "MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////////////////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEEeb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQNkFBAgEBA0IABGMxHraqggr2keTXszIcchTjYjH5WXpDaBOYgXva82mKcGnKgGRORXSmcjWN2suUCMkLQj3UNlZCFWF10wIrrlw=",
+ // },
+ // base64attestorPubKey:
+ // "MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////////////////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEEeb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQNkFBAgEBA0IABL+y43T1OJFScEep69/yTqpqnV/jzONz9Sp4TEHyAJ7IPN9+GHweCX1hT4OFxt152sBN3jJc1s0Ymzd8pNGZNoQ=",
+ // ticketIssuersUrlWebsitePrivateKey:
+ // "MIICSwIBADCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////////////////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEEeb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQNkFBAgEBBIIBVTCCAVECAQEEIM/T+SzcXcdtcNIqo6ck0nJTYzKL5ywYBFNSpI7R8AuBoIHjMIHgAgEBMCwGByqGSM49AQECIQD////////////////////////////////////+///8LzBEBCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcEQQR5vmZ++dy7rFWgYpXOhwsHApv82y3OKNlZ8oFbFvgXmEg62ncmo8RlXaT7/A4RCKj9F7RIpoVUGZxH0I/7ENS4AiEA/////////////////////rqu3OavSKA7v9JejNA2QUECAQGhRANCAARjMR62qoIK9pHk17MyHHIU42Ix+Vl6Q2gTmIF72vNpinBpyoBkTkV0pnI1jdrLlAjJC0I91DZWQhVhddMCK65c",
+ // },
+ // {
+ // collectionID: "NBA",
+ // onChain: true,
+ // contract: "A.0b2a3299cc857e29.TopShot",
+ // chain: "mainnet",
+ // blockchain: "flow"
+ // }
// {
// collectionID: "devcon",
// onChain: false,
@@ -167,20 +174,20 @@
// "MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////////////////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEEeb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQNkFBAgEBA0IABL+y43T1OJFScEep69/yTqpqnV/jzONz9Sp4TEHyAJ7IPN9+GHweCX1hT4OFxt152sBN3jJc1s0Ymzd8pNGZNoQ=",
// },
// {
- // hideToggle : false,
- // noTokenMsg: "If you have a token please:
1. Open your magic link inside this browser. 2. Refresh this page.
",
- // collectionID: 'devcon',
- // title: "Devcon",
- // onChain: false,
- // tokenOrigin: "http://localhost:3002/",
- // attestationOrigin: "https://attestation.id/",
- // unEndPoint: "https://crypto-verify.herokuapp.com/use-devcon-ticket",
- // image: "https://raw.githubusercontent.com/TokenScript/token-negotiator/main/mock-images/devcon.svg",
- // base64senderPublicKeys:
- // {
- // "AttestationDAO" : 'MFYwEAYHKoZIzj0CAQYFK...'
- // },
- // base64attestorPubKey: "MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////////////////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEEeb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQNkFBAgEBA0IABL+y43T1OJFScEep69/yTqpqnV/jzONz9Sp4TEHyAJ7IPN9+GHweCX1hT4OFxt152sBN3jJc1s0Ymzd8pNGZNoQ="
+ // hideToggle: false,
+ // noTokenMsg: "If you have a token please:
1. Open your magic link inside this browser. 2. Refresh this page.
",
+ // collectionID: 'devcon',
+ // title: "Devcon",
+ // onChain: false,
+ // tokenOrigin: "http://localhost:3002/",
+ // attestationOrigin: "https://attestation.id/",
+ // unEndPoint: "https://crypto-verify.herokuapp.com/use-devcon-ticket",
+ // image: "https://raw.githubusercontent.com/TokenScript/token-negotiator/main/mock-images/devcon.svg",
+ // base64senderPublicKeys:
+ // {
+ // "AttestationDAO": 'MFYwEAYHKoZIzj0CAQYFK...'
+ // },
+ // base64attestorPubKey: "MIIBMzCB7AYHKoZIzj0CATCB4AIBATAsBgcqhkjOPQEBAiEA/////////////////////////////////////v///C8wRAQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBEEEeb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuAIhAP////////////////////66rtzmr0igO7/SXozQNkFBAgEBA0IABL+y43T1OJFScEep69/yTqpqnV/jzONz9Sp4TEHyAJ7IPN9+GHweCX1hT4OFxt152sBN3jJc1s0Ymzd8pNGZNoQ="
// },
// {
// collectionID: "crypto-cowboy-country",
@@ -204,7 +211,7 @@
// blockchain: "solana",
// },
// {
- // hideToggle : false,
+ // hideToggle: false,
// collectionID: "pixel",
// collectionAddress: "166424b16c4c7f40eb852f9eb2d0d6869a9ed7ac54a8e6afbd516676bfbbeb19",
// onChain: true,
@@ -212,37 +219,37 @@
// chain: "mainnet",
// blockchain: "solana",
// },
- {
- onChain: true,
- collectionID: "expansion-punks",
- contract: "0x0d0167a823c6619d430b1a96ad85b888bcf97c37",
- chain: "eth",
- blockchain: "evm",
- },
- {
- onChain: true,
- collectionID: "fantom-collection-test",
- contract: "0x94e22c14118353651636f9af43cd0a5a08b93da3",
- chain: "fantom",
- blockchain: "evm",
- },
- {
- hideToggle: true,
- noTokenMsg:
- "If you have a token please:
1. Open your magic link inside this browser. 2. Refresh this page.
",
- onChain: true,
- collectionID: "bsc-collection-test",
- contract: "0xF5db804101d8600c26598A1Ba465166c33CdAA4b",
- chain: "bsc",
- blockchain: "evm",
- },
// {
// onChain: true,
- // collectionID: "tt",
- // contract: '0x76be3b62873462d2142405439777e971754e8e77',
- // chain: 'eth',
+ // collectionID: "expansion-punks",
+ // contract: "0x0d0167a823c6619d430b1a96ad85b888bcf97c37",
+ // chain: "eth",
// blockchain: "evm",
// },
+ // {
+ // onChain: true,
+ // collectionID: "fantom-collection-test",
+ // contract: "0x94e22c14118353651636f9af43cd0a5a08b93da3",
+ // chain: "fantom",
+ // blockchain: "evm",
+ // },
+ // {
+ // hideToggle: true,
+ // noTokenMsg:
+ // "If you have a token please:
1. Open your magic link inside this browser. 2. Refresh this page.
",
+ // onChain: true,
+ // collectionID: "bsc-collection-test",
+ // contract: "0xF5db804101d8600c26598A1Ba465166c33CdAA4b",
+ // chain: "bsc",
+ // blockchain: "evm",
+ // },
+ {
+ onChain: true,
+ collectionID: "tt",
+ contract: '0x76be3b62873462d2142405439777e971754e8e77',
+ chain: 'eth',
+ blockchain: "evm",
+ },
// { onChain: true, collectionID: "Perion", contract: '0x96af92ae2d822a0f191455ceca4d4e7ee227668e', chain: 'mumbai', blockchain: "evm" },
// { collectionID: 'COOLCATS-#2426-14', onChain: true, contract: '0x3C7e352481F4b2fdEc1e642a3f0018661c77513D', chain: 'eth', openSeaSlug: 'devcon-vi-suit-up-collection' },
// { collectionID: 'Town-Hall', onChain: true, contract: '0x81b30ff521D1fEB67EDE32db726D95714eb00637', chain: 'Optimism' },
@@ -259,11 +266,11 @@
// chain: "Arbitrum",
// },
// {
- // collectionID: "NBA",
- // onChain: true,
- // contract: "A.0b2a3299cc857e29.TopShot",
- // chain: "mainnet",
- // blockchain: "flow",
+ // collectionID: "NBA",
+ // onChain: true,
+ // contract: "A.0b2a3299cc857e29.TopShot",
+ // chain: "mainnet",
+ // blockchain: "flow",
// },
// {
// onChain: true,
@@ -276,35 +283,48 @@
// // Add fungible as a boolean
// // symbol: 'USX'
// },
- // {
- // contract: "0x107065a122f92636a1358a70a0efe0f1a080a7e5",
- // onChain: true,
- // fungible: true,
- // collectionID: "USX",
- // chain: "matic",
- // },
- // {
- // contract: "0x429f49faec3d568ef83ec803e02df78e25d5ee7d",
- // onChain: true,
- // fungible: true,
- // collectionID: "Ella",
- // chain: "matic",
- // },
- // {
- // contract: "0xaaa5b9e6c589642f98a1cda99b9d024b8407285a",
- // onChain: true,
- // fungible: true,
- // collectionID: "TITAN",
- // chain: "matic",
- // },
+ {
+ contract: "0x107065a122f92636a1358a70a0efe0f1a080a7e5",
+ onChain: true,
+ fungible: true,
+ collectionID: "USX",
+ chain: "matic",
+ },
+ {
+ contract: "0x429f49faec3d568ef83ec803e02df78e25d5ee7d",
+ onChain: true,
+ fungible: true,
+ collectionID: "Ella",
+ chain: "matic",
+ },
+ {
+ contract: "0xaaa5b9e6c589642f98a1cda99b9d024b8407285a",
+ onChain: true,
+ fungible: true,
+ collectionID: "TITAN",
+ chain: "matic",
+ },
],
uiOptions: {
openingHeading:
"Open a new world of perks, benefits and opportunities with your attestation, collectible or token.",
issuerHeading: "Get discount with Ticket",
- repeatAction: "try again",
+ repeatAction: "Retry",
+ dismissAction: 'Dismiss',
+ loadAction: 'Load Collection',
+ noTokensFoundEvent: 'No Tokens Found',
+ balancesFoundEvent: 'Balance Found',
+ nftsFoundEvent: 'Token(s) Found',
+ reDirectIssuerHeadingEvent: 'Connecting to Issuers...',
+ reDirectIssuerBodyEvent: "Your browser will re-direct shortly",
+ authenticationHeadingEvent: 'Authenticating...',
+ authenticationBodyEvent: "You may need to sign a new challenge in your wallet",
+ walletDidntConnectAction: "Wallet didn't connect",
+ cancelAction: "Cancel",
+ openingAction: "Let's Go!",
theme: theme,
position: "top-right",
+ userCancelIssuerAutoRedirectTimer: 2000,
},
unSupportedUserAgent: {
authentication: {
@@ -334,7 +354,13 @@
});
window.negotiator.on("tokens-selected", (tokens) => {
- console.log("tokens-selected", tokens);
+ console.log("tokens-selected 1", tokens);
+ });
+ window.negotiator.on("tokens-selected", (tokens) => {
+ console.log("tokens-selected 2", tokens);
+ });
+ window.negotiator.on("tokens-selected", (tokens) => {
+ console.log("tokens-selected 3", tokens);
});
window.negotiator.on("connected-wallet", (wallet) => {
console.log("connected wallet", wallet);
diff --git a/package-lock.json b/package-lock.json
index 0399de40..9313037f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,18 +1,18 @@
{
"name": "@tokenscript/token-negotiator",
- "version": "3.1.1",
+ "version": "3.2.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@tokenscript/token-negotiator",
- "version": "3.1.1",
+ "version": "3.2.0",
"license": "MIT",
"dependencies": {
"@onflow/fcl": "^1.3.2",
"@onflow/types": "^1.0.5",
"@peculiar/asn1-schema": "^2.2.0",
- "@tokenscript/attestation": "0.7.0-rc.1",
+ "@tokenscript/attestation": "0.7.0-rc.2",
"@toruslabs/torus-embed": "^2.2.5",
"@walletconnect/qrcode-modal": "^1.8.0",
"@walletconnect/types": "^2.1.5",
@@ -70,6 +70,14 @@
"webpack-cli": "^4.9.2"
}
},
+ "node_modules/@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/@ampproject/remapping": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
@@ -1818,21 +1826,21 @@
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz",
- "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==",
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz",
+ "integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==",
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
"node_modules/@eslint/eslintrc": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz",
- "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
+ "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
- "espree": "^9.5.1",
+ "espree": "^9.6.0",
"globals": "^13.19.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
@@ -1853,9 +1861,9 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/@eslint/eslintrc/node_modules/globals": {
- "version": "13.20.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
- "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+ "version": "13.22.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz",
+ "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==",
"dependencies": {
"type-fest": "^0.20.2"
},
@@ -1889,9 +1897,9 @@
}
},
"node_modules/@eslint/js": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz",
- "integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==",
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz",
+ "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
@@ -2711,9 +2719,9 @@
}
},
"node_modules/@humanwhocodes/config-array": {
- "version": "0.11.8",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
- "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+ "version": "0.11.11",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
+ "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
@@ -4669,9 +4677,9 @@
}
},
"node_modules/@tokenscript/attestation": {
- "version": "0.7.0-rc.1",
- "resolved": "https://registry.npmjs.org/@tokenscript/attestation/-/attestation-0.7.0-rc.1.tgz",
- "integrity": "sha512-FDA41LBbhYphXBDXl/kec4N7MLtBo91LLrwuF0I0PSnoC4Y4ZukbHl4lpVgSjGnu4vrs6QIjXRG2APsaScIYLw==",
+ "version": "0.7.0-rc.2",
+ "resolved": "https://registry.npmjs.org/@tokenscript/attestation/-/attestation-0.7.0-rc.2.tgz",
+ "integrity": "sha512-5C9KLU0cyT9gkvb6RYXZIlp/VWCZKq+Isev/aPtwcU/sR3N9ZYobJRQsayTUnxYERRq8YOmADxZjr7K2mCnEjQ==",
"hasInstallScript": true,
"dependencies": {
"@ethereum-attestation-service/eas-sdk": "^0.29.1",
@@ -4918,9 +4926,9 @@
}
},
"node_modules/@types/create-hash": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@types/create-hash/-/create-hash-1.2.2.tgz",
- "integrity": "sha512-Fg8/kfMJObbETFU/Tn+Y0jieYewryLrbKwLCEIwPyklZZVY2qB+64KFjhplGSw+cseZosfFXctXO+PyIYD8iZQ==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@types/create-hash/-/create-hash-1.2.3.tgz",
+ "integrity": "sha512-dh2NzQVdUY3+C3+ZnQ1T6JEa4RdyiWfTcL/MX/XfE8dCT//TG9rYDliDgPr3SJ9qnVek9Lzn1HCU/pH+KzrJXg==",
"dev": true,
"dependencies": {
"@types/node": "*"
@@ -6636,9 +6644,9 @@
}
},
"node_modules/acorn": {
- "version": "8.8.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
- "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==",
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
"bin": {
"acorn": "bin/acorn"
},
@@ -8907,26 +8915,26 @@
}
},
"node_modules/eslint": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz",
- "integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==",
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz",
+ "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.4.0",
- "@eslint/eslintrc": "^2.0.2",
- "@eslint/js": "8.38.0",
- "@humanwhocodes/config-array": "^0.11.8",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.2",
+ "@eslint/js": "8.50.0",
+ "@humanwhocodes/config-array": "^0.11.11",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
- "ajv": "^6.10.0",
+ "ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.1.1",
- "eslint-visitor-keys": "^3.4.0",
- "espree": "^9.5.1",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
"esquery": "^1.4.2",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
@@ -8934,22 +8942,19 @@
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
"globals": "^13.19.0",
- "grapheme-splitter": "^1.0.4",
+ "graphemer": "^1.4.0",
"ignore": "^5.2.0",
- "import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
- "js-sdsl": "^4.1.4",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
- "optionator": "^0.9.1",
+ "optionator": "^0.9.3",
"strip-ansi": "^6.0.1",
- "strip-json-comments": "^3.1.0",
"text-table": "^0.2.0"
},
"bin": {
@@ -9076,9 +9081,9 @@
}
},
"node_modules/eslint-visitor-keys": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz",
- "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==",
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
@@ -9148,15 +9153,18 @@
}
},
"node_modules/eslint/node_modules/eslint-scope": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
- "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
"node_modules/eslint/node_modules/estraverse": {
@@ -9266,13 +9274,13 @@
}
},
"node_modules/espree": {
- "version": "9.5.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz",
- "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==",
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
"dependencies": {
- "acorn": "^8.8.0",
+ "acorn": "^8.9.0",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.0"
+ "eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -10522,7 +10530,13 @@
"node_modules/grapheme-splitter": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
- "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
+ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "dev": true
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
},
"node_modules/har-schema": {
"version": "2.0.0",
@@ -15408,16 +15422,16 @@
}
},
"node_modules/optionator": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
- "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
"dependencies": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
"levn": "^0.4.1",
"prelude-ls": "^1.2.1",
- "type-check": "^0.4.0",
- "word-wrap": "^1.2.3"
+ "type-check": "^0.4.0"
},
"engines": {
"node": ">= 0.8.0"
@@ -18455,9 +18469,9 @@
"dev": true
},
"node_modules/ts-loader": {
- "version": "9.4.2",
- "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz",
- "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==",
+ "version": "9.4.4",
+ "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz",
+ "integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==",
"dev": true,
"dependencies": {
"chalk": "^4.1.0",
@@ -19727,6 +19741,7 @@
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -20054,6 +20069,11 @@
}
},
"dependencies": {
+ "@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA=="
+ },
"@ampproject/remapping": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
@@ -21283,18 +21303,18 @@
}
},
"@eslint-community/regexpp": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz",
- "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ=="
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz",
+ "integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ=="
},
"@eslint/eslintrc": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz",
- "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
+ "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
"requires": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
- "espree": "^9.5.1",
+ "espree": "^9.6.0",
"globals": "^13.19.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
@@ -21309,9 +21329,9 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"globals": {
- "version": "13.20.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
- "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+ "version": "13.22.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz",
+ "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==",
"requires": {
"type-fest": "^0.20.2"
}
@@ -21332,9 +21352,9 @@
}
},
"@eslint/js": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.38.0.tgz",
- "integrity": "sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g=="
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz",
+ "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ=="
},
"@ethereum-attestation-service/eas-contracts": {
"version": "0.27.1",
@@ -21816,9 +21836,9 @@
}
},
"@humanwhocodes/config-array": {
- "version": "0.11.8",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
- "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
+ "version": "0.11.11",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
+ "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
"requires": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
@@ -23328,9 +23348,9 @@
}
},
"@tokenscript/attestation": {
- "version": "0.7.0-rc.1",
- "resolved": "https://registry.npmjs.org/@tokenscript/attestation/-/attestation-0.7.0-rc.1.tgz",
- "integrity": "sha512-FDA41LBbhYphXBDXl/kec4N7MLtBo91LLrwuF0I0PSnoC4Y4ZukbHl4lpVgSjGnu4vrs6QIjXRG2APsaScIYLw==",
+ "version": "0.7.0-rc.2",
+ "resolved": "https://registry.npmjs.org/@tokenscript/attestation/-/attestation-0.7.0-rc.2.tgz",
+ "integrity": "sha512-5C9KLU0cyT9gkvb6RYXZIlp/VWCZKq+Isev/aPtwcU/sR3N9ZYobJRQsayTUnxYERRq8YOmADxZjr7K2mCnEjQ==",
"requires": {
"@ethereum-attestation-service/eas-sdk": "^0.29.1",
"@peculiar/asn1-schema": "^2.3.3",
@@ -23522,9 +23542,9 @@
}
},
"@types/create-hash": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@types/create-hash/-/create-hash-1.2.2.tgz",
- "integrity": "sha512-Fg8/kfMJObbETFU/Tn+Y0jieYewryLrbKwLCEIwPyklZZVY2qB+64KFjhplGSw+cseZosfFXctXO+PyIYD8iZQ==",
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@types/create-hash/-/create-hash-1.2.3.tgz",
+ "integrity": "sha512-dh2NzQVdUY3+C3+ZnQ1T6JEa4RdyiWfTcL/MX/XfE8dCT//TG9rYDliDgPr3SJ9qnVek9Lzn1HCU/pH+KzrJXg==",
"dev": true,
"requires": {
"@types/node": "*"
@@ -25009,9 +25029,9 @@
}
},
"acorn": {
- "version": "8.8.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz",
- "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA=="
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw=="
},
"acorn-globals": {
"version": "7.0.1",
@@ -26803,26 +26823,26 @@
}
},
"eslint": {
- "version": "8.38.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.38.0.tgz",
- "integrity": "sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==",
+ "version": "8.50.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz",
+ "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==",
"requires": {
"@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.4.0",
- "@eslint/eslintrc": "^2.0.2",
- "@eslint/js": "8.38.0",
- "@humanwhocodes/config-array": "^0.11.8",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.2",
+ "@eslint/js": "8.50.0",
+ "@humanwhocodes/config-array": "^0.11.11",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
- "ajv": "^6.10.0",
+ "ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
"debug": "^4.3.2",
"doctrine": "^3.0.0",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.1.1",
- "eslint-visitor-keys": "^3.4.0",
- "espree": "^9.5.1",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
"esquery": "^1.4.2",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
@@ -26830,22 +26850,19 @@
"find-up": "^5.0.0",
"glob-parent": "^6.0.2",
"globals": "^13.19.0",
- "grapheme-splitter": "^1.0.4",
+ "graphemer": "^1.4.0",
"ignore": "^5.2.0",
- "import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"is-path-inside": "^3.0.3",
- "js-sdsl": "^4.1.4",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
"lodash.merge": "^4.6.2",
"minimatch": "^3.1.2",
"natural-compare": "^1.4.0",
- "optionator": "^0.9.1",
+ "optionator": "^0.9.3",
"strip-ansi": "^6.0.1",
- "strip-json-comments": "^3.1.0",
"text-table": "^0.2.0"
},
"dependencies": {
@@ -26890,9 +26907,9 @@
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
},
"eslint-scope": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz",
- "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==",
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
"requires": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
@@ -27041,18 +27058,18 @@
}
},
"eslint-visitor-keys": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz",
- "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ=="
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="
},
"espree": {
- "version": "9.5.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz",
- "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==",
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
"requires": {
- "acorn": "^8.8.0",
+ "acorn": "^8.9.0",
"acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.0"
+ "eslint-visitor-keys": "^3.4.1"
}
},
"esprima": {
@@ -28121,7 +28138,13 @@
"grapheme-splitter": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
- "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
+ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "dev": true
+ },
+ "graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
},
"har-schema": {
"version": "2.0.0",
@@ -31788,16 +31811,16 @@
}
},
"optionator": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
- "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
"requires": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
"levn": "^0.4.1",
"prelude-ls": "^1.2.1",
- "type-check": "^0.4.0",
- "word-wrap": "^1.2.3"
+ "type-check": "^0.4.0"
}
},
"os-browserify": {
@@ -34048,9 +34071,9 @@
}
},
"ts-loader": {
- "version": "9.4.2",
- "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz",
- "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==",
+ "version": "9.4.4",
+ "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz",
+ "integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==",
"dev": true,
"requires": {
"chalk": "^4.1.0",
@@ -35033,7 +35056,8 @@
"word-wrap": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
- "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true
},
"wordwrapjs": {
"version": "4.0.1",
diff --git a/package.json b/package.json
index c8e88ef0..71828dd9 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "@tokenscript/token-negotiator",
- "version": "3.1.1",
- "description": "Token-negotiator a token attestation bridge between web 2.0 and 3.0.",
+ "version": "3.2.0",
+ "description": "Token Negotiator is an open source technology that enables you build web experiences for users around the tokens, collectibles, coins and attestations they hold. Enabled across EVM, Solana, Flow, Chiliz and EOS (Ultra). Create logic flows to enable personalized web experiences.",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
@@ -49,7 +49,7 @@
"@onflow/fcl": "^1.3.2",
"@onflow/types": "^1.0.5",
"@peculiar/asn1-schema": "^2.2.0",
- "@tokenscript/attestation": "0.7.0-rc.1",
+ "@tokenscript/attestation": "0.7.0-rc.2",
"@toruslabs/torus-embed": "^2.2.5",
"@walletconnect/qrcode-modal": "^1.8.0",
"@walletconnect/types": "^2.1.5",
diff --git a/src/__tests__/index.spec.ts b/src/__tests__/index.spec.ts
new file mode 100644
index 00000000..e332377a
--- /dev/null
+++ b/src/__tests__/index.spec.ts
@@ -0,0 +1,7 @@
+import { Client } from '../index'
+
+describe('src spec', () => {
+ test('ensure client is defined', async () => {
+ expect(Client).toBeDefined()
+ })
+})
diff --git a/src/client/__tests__/client.spec.ts b/src/client/__tests__/client.spec.ts
index b64efd86..1ecae022 100644
--- a/src/client/__tests__/client.spec.ts
+++ b/src/client/__tests__/client.spec.ts
@@ -1,17 +1,11 @@
// @ts-nocheck
import { AbstractAuthentication } from '../auth/abstractAuthentication'
-import { AttestedAddress } from '../auth/attestedAddress'
import { Client } from '../index'
import { TicketZKProof } from '../auth/ticketZKProof'
-import { URLNS } from '../../core/messaging'
-import { Outlet, defaultConfig } from '../../outlet/index'
-import { Client as client_2_0, Outlet as outlet_2_0 } from 'tn2_0'
-import { Client as client_2_2, Outlet as outlet_2_2 } from 'tn2_2'
import { OffChainTokenConfig } from '../interface'
+import { TextEncoder, TextDecoder } from 'util';
-function delay(time) {
- return new Promise((resolve) => setTimeout(resolve, time))
-}
+Object.assign(global, { TextDecoder, TextEncoder });
let tokenIssuer: OffChainTokenConfig = {
collectionID: 'devcon',
@@ -27,37 +21,8 @@ let tokenIssuer: OffChainTokenConfig = {
base64attestorPubKey: '',
}
-let tokenIssuer2: OffChainTokenConfig = {
- collectionID: 'edcon',
- title: 'Devcon',
- onChain: false,
- tokenOrigin: 'http://some.url/',
- attestationOrigin: 'https://stage.attestation.id/',
- unEndPoint: 'https://crypto-verify.herokuapp.com/use-devcon-ticket',
- image: 'https://raw.githubusercontent.com/TokenScript/token-negotiator/main/mock-images/devcon.svg',
- base64senderPublicKeys: {
- 10: 'MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAGJAHCiHbrCNAY9fAMdom4dGD6v/KkTIgRCkwLCjXFTkXWGrCEXHaZ8kWwdqlu0oYCrNQ2vdlqOl0s26/LzO8A==|MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAGJAHCiHbrCNAY9fAMdom4dGD6v/KkTIgRCkwLCjXFTkXWGrCEXHaZ8kWwdqlu0oYCrNQ2vdlqOl0s26/LzO8B==',
- },
- base64attestorPubKey: '',
-}
-
-let tokenIssuer3: OffChainTokenConfig = {
- collectionID: 'devconnect',
- title: 'Devcon',
- onChain: false,
- tokenOrigin: 'http://some.url/',
- attestationOrigin: 'https://stage.attestation.id/',
- unEndPoint: 'https://crypto-verify.herokuapp.com/use-devcon-ticket',
- image: 'https://raw.githubusercontent.com/TokenScript/token-negotiator/main/mock-images/devcon.svg',
- base64senderPublicKeys: {
- 55: 'MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAGJAHCiHbrCNAY9fAMdom4dGD6v/KkTIgRCkwLCjXFTkXWGrCEXHaZ8kWwdqlu0oYCrNQ2vdlqOl0s26/LzO8A==',
- },
- base64attestorPubKey: '',
-}
-
const config = {
type: 'passive',
- // requred to force redirect mode for Client 2.2
enableOffChainRedirectMode: true,
issuers: [tokenIssuer],
}
@@ -126,6 +91,7 @@ class LocalStorageMock {
}
describe('client spec', () => {
+
test('tokenNegotiatorClient a failed new instance of client - missing issuers key', () => {
let client = new Client({
type: 'passive',
@@ -137,6 +103,54 @@ describe('client spec', () => {
})
})
+ test('client library interface method switch theme can be used', () => {
+ const tokenNegotiatorClient = getOffChainConfigClient()
+ let client = new Client({
+ type: 'active',
+ issuers: tokenNegotiatorClient.getTokenStore().getCurrentIssuers(),
+ options: {},
+ })
+ client.negotiate().then(() => {
+ client.switchTheme('dark');
+ });
+ })
+
+ test('client library can add token via magic link', () => {
+ const tokenNegotiatorClient = getOffChainConfigClient()
+ let client = new Client({
+ type: 'active',
+ issuers: tokenNegotiatorClient.getTokenStore().getCurrentIssuers(),
+ options: {},
+ })
+ client.negotiate().then(() => {
+ client.addTokenViaMagicLink('https://outlet-stage.brandconnector.io/#type=asn&ticket=MIGTME0MATYCAgTUAgECBEEEDoIoocY3CRCPqbHzou84M7C-cWAfzrTnPwz7qWGWv3UVn53jWeTG8CSykYVk-SUdNwlGTiD-h8Xsf2cGzniNPQNCAK6Q153BMkBc9E2AR3MjEWNU_f-15Cl2w0EIvaqGoqYrHKSMm3CIjKik_KBRTzRrbQqM5lGe_jUivjhmfwR5HOUc&secret=6191295225924896663249851881131284553005448461740837624904453538708620527945&id=bob%40alice.com')
+ });
+ })
+
+ test('client can trigger promise to manage', () => {
+ const tokenNegotiatorClient = getOffChainConfigClient()
+ let client = new Client({
+ type: 'active',
+ issuers: tokenNegotiatorClient.getTokenStore().getCurrentIssuers(),
+ options: {},
+ })
+ client.negotiate().then(() => {
+ client.handleWalletRequired([]);
+ });
+ })
+
+ test('client can handle proof error', () => {
+ const tokenNegotiatorClient = getOffChainConfigClient()
+ let client = new Client({
+ type: 'active',
+ issuers: tokenNegotiatorClient.getTokenStore().getCurrentIssuers(),
+ options: {},
+ })
+ client.negotiate().then(() => {
+ client.handleProofError({ error: 'something went wrong' }, 'devconVi');
+ });
+ })
+
test('tokenNegotiatorClient a failed new instance of client - missing issuers length', () => {
let client = new Client({
type: 'passive',
@@ -313,21 +327,21 @@ describe('client spec', () => {
})
test('tokenNegotiatorClient on callback with event type tokens-selected ', () => {
+ const mockCallback = jest.fn();
const tokenNegotiatorClient = getOffChainConfigClient()
const event = 'tokens-selected'
- tokenNegotiatorClient.on(event, () => {
- logger(2, event)
- })
- expect(tokenNegotiatorClient.clientCallBackEvents[event]).toBeDefined()
+ tokenNegotiatorClient.on(event, mockCallback)
+ tokenNegotiatorClient.eventSender(event, { selectedTokens: {} });
+ expect(mockCallback).toHaveBeenCalled();
})
test('tokenNegotiatorClient on callback with event type tokens-loaded', () => {
+ const mockCallback = jest.fn();
const tokenNegotiatorClient = getOffChainConfigClient()
const event = 'tokens-loaded'
- tokenNegotiatorClient.on(event, () => {
- logger(2, event)
- })
- expect(tokenNegotiatorClient.clientCallBackEvents[event]).toBeDefined()
+ tokenNegotiatorClient.on(event, mockCallback);
+ tokenNegotiatorClient.eventSender(event, null);
+ expect(mockCallback).toHaveBeenCalled();
})
test('tokenNegotiatorClient on callback must have an event type', () => {
@@ -494,122 +508,5 @@ describe('client spec', () => {
const error = tokenNegotiatorClient.handleRecievedRedirectMessages()
expect(error).toEqual(null)
})
-})
-
-// TODO: Reimplement cross-version test for version 3.1
-/* describe('client spec cross-version', () => {
- let originalDocument = document
- let originalLocation = window.location
-
- const nonLocalUrl = 'https://non-local.url'
-
- beforeAll(() => {
- Object.defineProperty(global, 'document', {
- value: {
- location: {
- href: '',
- referrer: '',
- hash: '',
- search: '',
- origin: '',
- },
- addEventListener: () => {
- return true
- },
- },
- })
-
- Object.defineProperty(window, 'location', {
- value: {
- href: nonLocalUrl,
- origin: nonLocalUrl,
- },
- writable: true, // possibility to override
- })
-
- // required to force redirect mode
- window.navigator.brave = 1
- })
-
- afterAll(() => {
- Object.defineProperty(global, 'document', {
- value: originalDocument,
- })
-
- Object.defineProperty(window, 'location', {
- value: originalLocation,
- })
- })
-
- test('Outlet_2_0 save magicLink', async () => {
- let magicLinkParams =
- '?ticket=MIGTME0MATYCAgFNAgEBBEEEF6_tKK2dCfLQiwS4FuqmiQDVrafJ05vCOkYN4iT28JULCClrvI2_kGTxrL12sXlH9w9mohLQlMdmaWvFzaZVlgNCAKu7SESOLf7L5sjZPcTQVkAu9YTC88mNK8oyUjiP2gsnTUxr0BGr0eWSTYmbDqNlX3JXOEqvEH39LEQjWsXn44oc&secret=45845870684&mail=oleh.hryb.us@gmail.com'
-
- window.location.search = magicLinkParams
-
- // LocalStorage must be empty
- expect(localStorage.getItem(defaultConfig.itemStorageKey)).toBe(null)
- new outlet_2_0(tokenIssuer)
-
- expect(localStorage.getItem(defaultConfig.itemStorageKey)).toContain(magicLinkParams)
- })
-
- test('Redirect Client_lastest -> Outlet 2.2', async () => {
- let client = new Client(config)
-
- // prepare Redirect URL
- await client.negotiate()
- let url = new URL(window.location.href)
- expect(new URLSearchParams(url.hash.substring(1)).get('action')).toBe('get-issuer-tokens')
-
- window.location.hash = url.hash
- new outlet_2_2(tokenIssuer)
-
- // need delay, because pageOnLoadEventHandler() is async
- await delay(1000)
-
- let hash = new URL(document.location.href).hash.substring(1)
- expect(new URLSearchParams(hash).get('action')).toBe('get-issuer-tokens-response')
- })
- test('Redirect Client_2.2 -> Outlet_lastest', async () => {
- let client = new client_2_2(config)
-
- window.location.hash = ''
- window.location.href = 'http://localhost'
- document.location.hash = ''
- document.location.href = 'http://localhost'
- // prepare Redirect URL
- await client.negotiate()
- let url = new URL(document.location.href)
- expect(new URLSearchParams(url.hash.substring(1)).get('action')).toBe('get-issuer-tokens')
-
- // console.log(
- // `window.location.hash = "${window.location.hash}",
- // window.location.href = "${window.location.href}",
- // document.location.hash = "${document.location.hash}",
- // document.location.href = "${document.location.href}",`
- // )
-
- window.location.hash = url.hash
- document.location.hash = url.hash
- document.referrer = nonLocalUrl
- localStorage.setItem('tn-whitelist', '{"https://non-local.url":{"type":"read"}}')
-
- new Outlet(tokenIssuer)
-
- // need delay, because pageOnLoadEventHandler() is async
- await delay(1000)
-
- let hash = new URL(window.location.href).hash.substring(1)
- expect(new URLSearchParams(hash).get('action')).toBe('get-issuer-tokens-response')
- })
-
- test('tokenNegotiatorClient read prefixed param', async () => {
- window.location.hash = `p1=1&${URLNS}p2=2`
- let client = getOffChainConfigClient()
-
- expect(client.getDataFromQuery('p2')).toBe('2')
- expect(client.getDataFromQuery('p1')).toBe('1')
- })
-})*/
+})
diff --git a/src/client/__tests__/eventHookHandler.spec.ts b/src/client/__tests__/eventHookHandler.spec.ts
new file mode 100644
index 00000000..a6e2feba
--- /dev/null
+++ b/src/client/__tests__/eventHookHandler.spec.ts
@@ -0,0 +1,27 @@
+// @ts-nocheck
+import { EventHookHandler } from '../eventHookHandler'
+
+const mockCallback = jest.fn();
+
+describe('client spec', () => {
+ test('eventHookHandler can subscribe and trigger events', async () => {
+ const eventHookHandler = new EventHookHandler()
+ eventHookHandler.subscribe('selected-tokens', mockCallback);
+ eventHookHandler.trigger('selected-tokens', { data: "It's not a bug; it's an undocumented feature" });
+ expect(mockCallback).toHaveBeenCalled();
+ })
+ test('eventHookHandler can unsubscribe', async () => {
+ const eventHookHandler = new EventHookHandler()
+ const unsubscribe = eventHookHandler.subscribe('selected-tokens', mockCallback);
+ unsubscribe();
+ eventHookHandler.trigger('selected-tokens', { data: "Make it work, make it right, make it fast." });
+ expect(mockCallback).toHaveLength(0);
+ })
+ test('eventHookHandler can unsubscribe all subscriptions of event type', async () => {
+ const eventHookHandler = new EventHookHandler()
+ eventHookHandler.subscribe('selected-tokens', mockCallback);
+ eventHookHandler.unsubscribe('selected-tokens');
+ eventHookHandler.trigger('selected-tokens', { data: "Make it work, make it right, make it fast." });
+ expect(mockCallback).toHaveLength(0);
+ })
+})
\ No newline at end of file
diff --git a/src/client/__tests__/selectIssuers.spec.ts b/src/client/__tests__/selectIssuers.spec.ts
index 2985b80e..4647c950 100644
--- a/src/client/__tests__/selectIssuers.spec.ts
+++ b/src/client/__tests__/selectIssuers.spec.ts
@@ -22,7 +22,19 @@ describe('select issuers spec', () => {
blockchain: 'evm',
},
],
- options: {},
+ uiOptions: {
+ openingHeading:
+ "Open a new world of perks, benefits and opportunities with your attestation, collectible or token.",
+ issuerHeading: "Get discount with Ticket",
+ repeatAction: "Retry",
+ dismissAction: 'Dismiss',
+ loadAction: 'Load Collection',
+ noTokensFoundEvent: 'No Tokens Found',
+ balancesFoundEvent: 'Balance Found',
+ nftsFoundEvent: 'Token(s) Found',
+ openingAction: "Let's Go!",
+ position: "top-right",
+ },
})
}
diff --git a/src/client/auth/abstractAuthentication.ts b/src/client/auth/abstractAuthentication.ts
index bf3fd098..e1c7e307 100644
--- a/src/client/auth/abstractAuthentication.ts
+++ b/src/client/auth/abstractAuthentication.ts
@@ -1,5 +1,6 @@
import { AuthenticateInterface, MultiTokenInterface, OffChainTokenConfig, OnChainTokenConfig } from '../interface'
import { Client } from '../index'
+import { LOCAL_STORAGE_PROOF_KEY } from '../../constants'
export interface AuthenticationResult {
type: string
@@ -40,8 +41,6 @@ export interface AuthenticationMethodMulti {
export abstract class AbstractAuthentication {
public abstract TYPE: string
- public static STORAGE_KEY = 'tn-proof'
-
protected client: Client
constructor(client?: Client) {
@@ -58,7 +57,7 @@ export abstract class AbstractAuthentication {
challenges[this.getFullKey(key)] = proof
- localStorage.setItem(AbstractAuthentication.STORAGE_KEY, JSON.stringify(challenges))
+ localStorage.setItem(LOCAL_STORAGE_PROOF_KEY, JSON.stringify(challenges))
}
protected getSavedProof(key: string) {
@@ -78,7 +77,7 @@ export abstract class AbstractAuthentication {
if (challenges[fullKey]) delete challenges[fullKey]
- localStorage.setItem(AbstractAuthentication.STORAGE_KEY, JSON.stringify(challenges))
+ localStorage.setItem(LOCAL_STORAGE_PROOF_KEY, JSON.stringify(challenges))
}
private getFullKey(key: string) {
@@ -86,8 +85,7 @@ export abstract class AbstractAuthentication {
}
private getProofs(): { [key: string]: AuthenticationResult } {
- const data = localStorage.getItem(AbstractAuthentication.STORAGE_KEY)
-
- return data && data.length ? JSON.parse(data) : {}
+ const data = localStorage.getItem(LOCAL_STORAGE_PROOF_KEY)
+ return data?.length ? JSON.parse(data) : {}
}
}
diff --git a/src/client/auth/signedUNChallenge.ts b/src/client/auth/signedUNChallenge.ts
index 37cc4195..0e476ce7 100644
--- a/src/client/auth/signedUNChallenge.ts
+++ b/src/client/auth/signedUNChallenge.ts
@@ -34,7 +34,7 @@ export class SignedUNChallenge extends AbstractAuthentication implements Authent
}
}
- if (!currentProof){
+ if (!currentProof) {
let walletConnection = connection.provider
currentProof = {
diff --git a/src/client/auth/ticketZKProof.ts b/src/client/auth/ticketZKProof.ts
index cb996da7..7fadedb3 100644
--- a/src/client/auth/ticketZKProof.ts
+++ b/src/client/auth/ticketZKProof.ts
@@ -10,7 +10,7 @@ import { shouldUseRedirectMode } from '../../utils/support/getBrowserData'
import { EasZkProof } from '@tokenscript/attestation/dist/eas/EasZkProof'
import { DEFAULT_EAS_SCHEMA, TokenType } from '../../outlet/ticketStorage'
import { OutletIssuerInterface } from '../../outlet/interfaces'
-import { DEFAULT_RPC_MAP } from '../../core/constants'
+import { DEFAULT_RPC_MAP } from '../../constants'
export class TicketZKProof extends AbstractAuthentication implements AuthenticationMethod {
TYPE = 'ticketZKProof'
@@ -123,7 +123,14 @@ export class TicketZKProof extends AbstractAuthentication implements Authenticat
if (type === 'eas') {
const schema = issuerConfig.eas ? { fields: issuerConfig.eas.fields } : DEFAULT_EAS_SCHEMA
const easZkProof = new EasZkProof(schema, { ...DEFAULT_RPC_MAP, ...ethRPCMap })
- await easZkProof.validateUseTicket(proof, issuerConfig.base64attestorPubKey, issuerConfig.base64senderPublicKeys, ethAddress)
+ await easZkProof.validateUseTicket(
+ proof,
+ issuerConfig.base64attestorPubKey,
+ issuerConfig.base64senderPublicKeys,
+ ethAddress,
+ 'asn',
+ 'asn',
+ )
} else {
Authenticator.validateUseTicket(proof, issuerConfig.base64attestorPubKey, issuerConfig.base64senderPublicKeys, ethAddress)
}
diff --git a/src/client/auth/util/SafeConnect.ts b/src/client/auth/util/SafeConnect.ts
index f2f829fd..a6b75dc9 100644
--- a/src/client/auth/util/SafeConnect.ts
+++ b/src/client/auth/util/SafeConnect.ts
@@ -1,6 +1,7 @@
import { KeyStore } from '@tokenscript/attestation/dist/safe-connect/KeyStore'
import { uint8tohex } from '@tokenscript/attestation/dist/libs/utils'
import { EthereumKeyLinkingAttestation } from '@tokenscript/attestation/dist/safe-connect/EthereumKeyLinkingAttestation'
+import { HOLDING_KEY_ALGORITHM } from '../../../constants'
export interface ChallengeInterface {
expiry: number
@@ -24,17 +25,16 @@ export interface ProofResponseInterface {
}
export class SafeConnect {
- public static HOLDING_KEY_ALGORITHM = 'RSASSA-PKCS1-v1_5'
public static keyStore = new KeyStore()
public static async getLinkPrivateKey() {
- let keys = await SafeConnect.keyStore.getOrCreateKey(SafeConnect.HOLDING_KEY_ALGORITHM)
+ let keys = await SafeConnect.keyStore.getOrCreateKey(HOLDING_KEY_ALGORITHM)
return keys.attestHoldingKey.privateKey
}
public static async getLinkPublicKey() {
- let keys = await SafeConnect.keyStore.getOrCreateKey(SafeConnect.HOLDING_KEY_ALGORITHM)
+ let keys = await SafeConnect.keyStore.getOrCreateKey(HOLDING_KEY_ALGORITHM)
return uint8tohex(keys.holdingPubKey)
}
diff --git a/src/client/auth/util/UN.ts b/src/client/auth/util/UN.ts
index b874d238..d714cc38 100644
--- a/src/client/auth/util/UN.ts
+++ b/src/client/auth/util/UN.ts
@@ -3,6 +3,7 @@ import { sign } from 'tweetnacl'
import { base58ToUint8Array, hexStringToUint8Array, strToHexStr, strToUtfBytes } from '../../../utils'
import * as flowTypes from '@onflow/types'
import { ecc } from 'eosjs/dist/eosjs-ecc-migration'
+import { DEFAULT_UN_ENDPOINT, COMMON_UN_API_KEY } from '../../../constants';
export interface UNInterface {
expiration: number
@@ -17,21 +18,17 @@ export interface UNInterface {
}
export class UN {
- private static DEFAULT_ENDPOINT = 'https://api.smarttokenlabs.com/un'
- private static COMMON_API_KEY =
- 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9qZWN0IjoidG9rZW4tbmVnb3RpYXRvciIsImlhdCI6MTY4OTc1NzQ4Nn0.ELE1OVvVFY1yrWlbnxtQur6dgeVxmKlPb9LZ_8cMOs8'
-
public static async getNewUN(endPoint: string): Promise {
try {
let response
if (endPoint) {
response = await fetch(endPoint)
} else {
- response = await fetch(this.DEFAULT_ENDPOINT, {
+ response = await fetch(DEFAULT_UN_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
- 'x-stl-key': this.COMMON_API_KEY,
+ 'x-stl-key': COMMON_UN_API_KEY,
},
body: JSON.stringify({ targetDomain: window.location.origin }),
})
diff --git a/src/client/eventHookHandler.ts b/src/client/eventHookHandler.ts
new file mode 100644
index 00000000..1c86c5b6
--- /dev/null
+++ b/src/client/eventHookHandler.ts
@@ -0,0 +1,34 @@
+export class EventHookHandler {
+ subIds: number
+ subscriptions: { [eventName: string]: { [token: number]: (data: any) => void } }
+
+ constructor() {
+ this.subIds = 0
+ this.subscriptions = {}
+ }
+
+ subscribe(eventName: string, fn: (data: any) => void) {
+ if (!this.subscriptions[eventName]) this.subscriptions[eventName] = {}
+ const token = ++this.subIds
+ this.subscriptions[eventName][token] = fn
+ return () => this.unsubscribe(eventName, token)
+ }
+
+ unsubscribe(eventName: string, token: number) {
+ if (!token) delete this.subscriptions[eventName]
+ this.subscriptions[eventName] && delete this.subscriptions[eventName][token]
+ }
+
+ trigger(eventName: string, data: any) {
+ this.publish(eventName, data)
+ return data
+ }
+
+ publish(eventName, data) {
+ const subs = this.subscriptions[eventName]
+ if (!subs) {
+ return false
+ }
+ Object.values(subs).forEach((sub) => sub(data))
+ }
+}
diff --git a/src/client/index.ts b/src/client/index.ts
index 5e9c38a0..3e1aa689 100644
--- a/src/client/index.ts
+++ b/src/client/index.ts
@@ -8,6 +8,7 @@ import {
removeUrlSearchParams,
createIssuerHashMap,
createIssuerHashArray,
+ sleep,
} from '../utils'
import { getNftCollection, getNftTokens } from '../utils/token/nftProvider'
import { TokenStore } from './tokenStore'
@@ -32,9 +33,12 @@ import { shouldUseRedirectMode } from '../utils/support/getBrowserData'
import { VERSION } from '../version'
import { getFungibleTokenBalances, getFungibleTokensMeta } from '../utils/token/fungibleTokenProvider'
import { URLNS } from '../core/messaging'
-import { DecodedToken, TokenType } from '../outlet/ticketStorage'
+import { TokenType } from '../outlet/ticketStorage'
import { MultiTokenAuthRequest, MultiTokenAuthResult, OutletIssuerInterface, ProofResult } from '../outlet/interfaces'
import { AttestationIdClient } from '../outlet/attestationIdClient'
+import { EventHookHandler } from './eventHookHandler'
+import { ethers } from 'ethers'
+import { TokenListItemInterface } from './views/token-list'
if (typeof window !== 'undefined') window.tn = { VERSION }
@@ -104,14 +108,20 @@ export class Client {
public config: NegotiationInterface
private web3WalletProvider: Web3WalletProvider
private messaging: Messaging
+ private eventHookHandler: EventHookHandler
protected ui: UiInterface
- private clientCallBackEvents: { [key: string]: (data: any) => Promise | void } = {}
protected tokenStore: TokenStore
+ public externalUtils = {
+ evm: {
+ ethers,
+ },
+ }
private uiUpdateCallbacks: { [type in UIUpdateEventType] } = {
[UIUpdateEventType.ISSUERS_LOADING]: undefined,
[UIUpdateEventType.ISSUERS_LOADED]: undefined,
[UIUpdateEventType.WALLET_DISCONNECTED]: undefined,
}
+ private userCancelTokenAutoload: boolean
private urlParams: URLSearchParams
@@ -120,6 +130,8 @@ export class Client {
}*/
constructor(config: NegotiationInterface) {
+ this.eventHookHandler = new EventHookHandler()
+
if (window.location.hash) {
this.urlParams = new URLSearchParams(window.location.hash.substring(1))
let action = this.getDataFromQuery('action')
@@ -131,6 +143,9 @@ export class Client {
this.config = this.mergeConfig(defaultConfig, config)
+ // TODO investigate if this works correctly.
+ this.config.autoLoadTokens = localStorage.getItem('tn-autoload-tokens') === 'false' ? false : this.config.autoLoadTokens
+
this.tokenStore = new TokenStore(this.config.autoEnableTokens, this.config.tokenPersistenceTTL)
// @ts-ignore
if (this.config.issuers?.length > 0) this.tokenStore.updateIssuers(this.config.issuers)
@@ -292,20 +307,18 @@ export class Client {
// TODO: Move to token store OR select-wallet view - this method is very similar to getCurrentBlockchains()
public hasIssuerForBlockchain(blockchain: 'evm' | 'solana' | 'flow' | 'ultra', useOauth = false) {
+ const _blockchain = blockchain.toLocaleLowerCase()
return (
this.config.issuers.filter((issuer: OnChainTokenConfig) => {
- const oAuthIssuer = useOauth && issuer.oAuth2options
- if (blockchain === 'evm' && !issuer.onChain) {
- return true
- }
- if (blockchain === 'solana' && typeof window.solana === 'undefined') {
- return false
- }
- if (blockchain === 'ultra' && typeof window.ultra === 'undefined') {
- return false
- }
- const blockChainNameMatch = issuer.blockchain ? issuer.blockchain.toLowerCase() : 'evm' === blockchain
- return blockChainNameMatch && (oAuthIssuer || !issuer.oAuth2options)
+ const issuerBlockChain = issuer.blockchain?.toLocaleLowerCase()
+ const blockChainUsed = issuerBlockChain === blockchain
+ const solanaEnabled = blockChainUsed && _blockchain === 'solana' && typeof window.solana !== 'undefined'
+ const ultraEnabled = blockChainUsed && _blockchain === 'ultra' && typeof window.ultra !== 'undefined'
+ const flowEnabled = blockChainUsed && _blockchain === 'flow'
+ const evmEnabled = blockChainUsed && _blockchain === 'evm' && !issuer.oAuth2options && !useOauth
+ const sociosEnabled = blockChainUsed && _blockchain === 'evm' && issuer.oAuth2options && useOauth
+ const fallBackToEVM = _blockchain === 'evm' && !issuerBlockChain && !useOauth
+ return solanaEnabled || ultraEnabled || evmEnabled || sociosEnabled || flowEnabled || fallBackToEVM
}).length > 0
)
}
@@ -326,6 +339,7 @@ export class Client {
this.tokenStore.clearCachedTokens()
this.eventSender('connected-wallet', null)
this.eventSender('disconnected-wallet', null)
+ localStorage.removeItem('tn-autoload-tokens')
this.triggerUiUpdateCallback(UIUpdateEventType.WALLET_DISCONNECTED)
} catch (e) {
logger(2, 'Failed to disconnect wallet', e)
@@ -465,14 +479,17 @@ export class Client {
autoOpenPopup = true
}
- if (this.config.autoEnableTokens && Object.keys(this.tokenStore.getSelectedTokens()).length)
- this.eventSender('tokens-selected', {
- selectedTokens: this.tokenStore.getSelectedTokens(),
- })
-
+ if (this.config.autoEnableTokens && Object.keys(this.tokenStore.getSelectedTokens()).length) this.tokensSelectedCallBackHandler()
if (openPopup || (this.config.uiOptions.autoPopup === true && autoOpenPopup)) this.ui.openOverlay()
}
+ private tokensSelectedCallBackHandler = () => {
+ this.eventSender('tokens-selected', {
+ selectedTokens: this.tokenStore.getSelectedTokens(),
+ selectedIssuerKeys: Object.keys(this.tokenStore.getSelectedTokens()),
+ })
+ }
+
private cancelAutoload = true
async tokenAutoLoad(onLoading: (issuer: string) => void, onComplete: (issuer: string, tokens: any[]) => void, refresh: boolean) {
@@ -515,10 +532,16 @@ export class Client {
}
this.eventSender('tokens-loaded', { loadedCollections: Object.keys(this.tokenStore.getCurrentIssuers()).length })
+
+ // use retry logic here too
+ // document.querySelectorAll('.connect-btn-tn .lds-ellipsis').forEach((el) => {
+ // el.parentElement.innerHTML = this.config.uiOptions?.loadAction ?? 'Load Collection'
+ // })
}
- cancelTokenAutoload() {
+ public cancelTokenAutoload() {
this.cancelAutoload = true
+ localStorage.setItem('tn-autoload-tokens', 'false')
}
async setPassiveNegotiationWebTokens(): Promise {
@@ -556,7 +579,7 @@ export class Client {
}
}
} catch (error) {
- errorHandler('popup error', 'error', () => this.eventSender('error', { issuer, error }), null, true, false)
+ errorHandler(error, 'error', () => this.eventSender('error', { issuer, error }), null, true, false)
continue
}
@@ -655,12 +678,8 @@ export class Client {
logger(2, 'Emit tokens')
logger(2, tokens)
- for (let issuer in tokens) {
- tokens[issuer] = { tokens: tokens[issuer] }
- }
-
- this.eventSender('tokens-selected', { selectedTokens: tokens })
- this.eventSender('tokens-loaded', { loadedCollections: Object.keys(tokens).length })
+ this.tokensSelectedCallBackHandler()
+ this.eventSender('tokens-loaded', { loadedCollections: Object.keys(this.tokenStore.getCurrentTokens()).length })
// Feature not supported when an end users third party cookies are disabled
// because the use of a tab requires a user gesture.
@@ -683,9 +702,7 @@ export class Client {
}
if (this.config.autoEnableTokens) {
- this.eventSender('tokens-selected', {
- selectedTokens: this.tokenStore.getSelectedTokens(),
- })
+ this.tokensSelectedCallBackHandler()
}
return tokens
@@ -710,6 +727,7 @@ export class Client {
tokens.map((token) => {
token.walletAddress = walletAddress
+ token.collectionId = issuer.collectionID
return token
})
@@ -750,9 +768,24 @@ export class Client {
private async loadRemoteOutletTokens(issuer: OffChainTokenConfig): Promise {
const redirectRequired = shouldUseRedirectMode(this.config.offChainRedirectMode)
-
if (redirectRequired) this.tokenStore.setTokens(issuer.collectionID, [])
-
+ if (this.ui && !issuer.onChain) {
+ await waitForElementToExist('.load-container-tn')
+ this.ui.showLoader(
+ `${this.config.uiOptions?.reDirectIssuerEventHeading ?? 'Connecting to Issuers...'} `,
+ `${this.config.uiOptions?.reDirectIssuerBodyEvent ?? 'Your browser will re-direct shortly'} `,
+ `${
+ this.config.uiOptions?.cancelAction ?? 'Cancel'
+ } `,
+ )
+ this.enableTokenAutoLoadCancel()
+ this.eventSender('page-redirecting', { collectionId: issuer.collectionID, tokenOrigin: issuer.tokenOrigin })
+ await sleep(this.config.uiOptions.userCancelIssuerAutoRedirectTimer ?? 2500)
+ if (this.userCancelTokenAutoload) {
+ this.userCancelTokenAutoload = false
+ return {}
+ }
+ }
const res = await this.messaging.sendMessage(
{
action: OutletAction.GET_ISSUER_TOKENS,
@@ -765,9 +798,7 @@ export class Client {
this.config.type === 'active' ? this.ui : null,
redirectRequired ? window.location.href : false,
)
-
if (!res) return // Site is redirecting
-
return res.data?.tokens ?? {}
}
@@ -782,17 +813,20 @@ export class Client {
updateSelectedTokens(selectedTokens) {
this.tokenStore.setSelectedTokens(selectedTokens)
- this.eventSender('tokens-selected', { selectedTokens })
+ this.tokensSelectedCallBackHandler()
}
async prepareToAuthenticateToken(authRequest: AuthenticateInterface) {
await this.checkUserAgentSupport('authentication')
- const { unsignedToken, tokenId } = authRequest
- if (!authRequest.issuer) authRequest.issuer = unsignedToken?.collectionId
- requiredParams(authRequest.issuer && (unsignedToken || tokenId), 'Issuer and unsigned token required.')
- const config = this.tokenStore.getCurrentIssuers()[authRequest.issuer]
+ let unsignedToken = authRequest?.unsignedToken
+ if (!unsignedToken && authRequest.collectionId) unsignedToken = authRequest
+ const issuer = authRequest?.issuer ?? unsignedToken?.collectionId ?? authRequest?.collectionId
+ const tokenId = authRequest?.tokenId
+ console.log('tokenIssuer', issuer, 'unsignedToken', unsignedToken)
+ requiredParams(issuer && (unsignedToken || tokenId), 'Issuer and unsigned token required MUTLI.')
+ const config = this.tokenStore.getCurrentIssuers()[issuer]
if (!config) errorHandler('Provided issuer was not found.', 'error', null, null, true, true)
- return authRequest
+ return { unsignedToken, issuer, tokenId }
}
async getMultiRequestBatch(authRequests: AuthenticateInterface[]) {
@@ -800,41 +834,29 @@ export class Client {
onChain: {},
offChain: {},
}
- // build a list of the batches for each token origin. At this point when this loop is complete
- // we will have a list of all the tokens that need to be authenticated and the origin they need to be authenticated against.
await Promise.all(
authRequests.map(async (authRequestItem) => {
const reqItem = await this.prepareToAuthenticateToken(authRequestItem)
-
- if (!reqItem.tokenId && reqItem.unsignedToken?.tokenId) reqItem.tokenId = reqItem.unsignedToken?.tokenId
-
const issuerConfig = this.tokenStore.getCurrentIssuers()[reqItem.issuer] as OffChainTokenConfig
- // Off Chain
- // Setup for Token Collection. e.g. authRequestBatch.offChain['https://mywebsite.com']['devcon']
- /**
- * Always generate a batch
- */
if (issuerConfig.onChain === false) {
if (!authRequestBatch.offChain[issuerConfig.tokenOrigin]) authRequestBatch.offChain[issuerConfig.tokenOrigin] = {}
-
if (!authRequestBatch.offChain[issuerConfig.tokenOrigin][reqItem.issuer]) {
authRequestBatch.offChain[issuerConfig.tokenOrigin][reqItem.issuer] = {
tokenIds: [],
issuerConfig: issuerConfig,
}
}
- // Push token into the request batch
- authRequestBatch.offChain[issuerConfig.tokenOrigin][reqItem.issuer].tokenIds.push(reqItem.tokenId)
+ authRequestBatch.offChain[issuerConfig.tokenOrigin][reqItem.issuer].tokenIds.push(
+ reqItem.tokenId ?? reqItem.unsignedToken.tokenId,
+ )
return
}
-
throw new Error('On-chain token are not supported by batch authentication at this time.')
}),
)
-
- if (Object.keys(authRequestBatch.offChain).length > 1)
+ if (Object.keys(authRequestBatch.offChain).length > 1) {
throw new Error('Only a single token origin is supported by batch authentication at this time.')
-
+ }
return authRequestBatch
}
@@ -845,19 +867,21 @@ export class Client {
if (this.ui) {
this.ui.showLoaderDelayed(
[
- 'Authenticating... ',
- 'You may need to sign a new challenge in your wallet ',
- "Cancel ",
+ `${this.config.uiOptions?.authenticationHeadingEvent ?? 'Authenticating...'} `,
+ `${this.config.uiOptions?.authenticationBodyEvent ?? 'You may need to sign a new challenge in your wallet'} `,
+ `${
+ this.config.uiOptions?.cancelAction ?? 'Cancel'
+ } `,
],
600,
true,
)
+ this.enableAuthCancel(authRequests)
}
const authRequestBatch = await this.getMultiRequestBatch(authRequests)
let issuerProofs = {}
- // Send the request batches to each token origin:
// Off Chain: // ['https://devcon.com']['issuer'][list of tokenIds]
for (const tokenOrigin in authRequestBatch.offChain) {
let AuthType = TicketZKProofMulti
@@ -875,7 +899,7 @@ export class Client {
if (err.message === 'WALLET_REQUIRED') {
return this.handleWalletRequired(authRequest)
}
- // errorHandler(err, 'error', () => this.handleProofError(err, `multi issuer authentication via ${tokenOrigin}`), null, false, true)
+ // errorHandler(err, 'error', () => this.handleProofError(err, `multi issuer authentication via ${ tokenOrigin }`), null, false, true)
console.error(err)
throw err
}
@@ -900,13 +924,15 @@ export class Client {
else return this.authenticateToken(authRequest as AuthenticateInterface)
}
- async authenticateToken(authRequest: AuthenticateInterface) {
+ async authenticateToken(authRequest: AuthenticateInterface | any) {
await this.checkUserAgentSupport('authentication')
- const { unsignedToken, issuer } = authRequest
- const tokenIssuer = issuer ?? unsignedToken.collectionId
+ const tokenIssuer = authRequest.issuer ?? authRequest.unsignedToken?.collectionId ?? authRequest.collectionId
+ let unsignedToken = authRequest.unsignedToken
+ if (!unsignedToken && authRequest.collectionId) unsignedToken = authRequest
- requiredParams(tokenIssuer && unsignedToken, 'Issuer and unsigned token required.')
+ console.log('tokenIssuer', tokenIssuer, 'unsignedToken', unsignedToken)
+ requiredParams(tokenIssuer && unsignedToken, 'Issuer and unsigned token required. SINGLE')
const config = this.tokenStore.getCurrentIssuers()[tokenIssuer]
@@ -917,9 +943,11 @@ export class Client {
if (this.ui) {
this.ui.showLoaderDelayed(
[
- 'Authenticating... ',
- 'You may need to sign a new challenge in your wallet ',
- "Cancel ",
+ `${this.config.uiOptions?.authenticationHeadingEvent ?? 'Authenticating...'} `,
+ `${this.config.uiOptions?.authenticationBodyEvent ?? 'You may need to sign a new challenge in your wallet'} `,
+ `${
+ this.config.uiOptions?.cancelAction ?? 'Cancel'
+ } `,
],
600,
true,
@@ -980,6 +1008,7 @@ export class Client {
cancelAuthButton.onclick = () => {
const err = 'User cancelled authentication'
this.ui.showError(err)
+ this.eventSender('user-cancel', { eventType: 'authentication' })
this.eventSender('token-proof', { issuer, error: err, data: null })
}
})
@@ -988,6 +1017,26 @@ export class Client {
})
}
+ public enableTokenAutoLoadCancel(): void {
+ waitForElementToExist('.cancel-autoload-btn')
+ .then((cancelAuthButton: HTMLElement) => {
+ cancelAuthButton.onclick = () => {
+ this.userCancelTokenAutoload = true
+ this.cancelTokenAutoload()
+ this.ui.dismissLoader()
+ // TODO implement communication (pub/sub events)
+ // to de-couple this logic for default and custom views to utilise.
+ document.querySelectorAll('.connect-btn-tn .lds-ellipsis').forEach((el) => {
+ el.parentElement.innerHTML = this.config.uiOptions?.loadAction ?? 'Load Collection'
+ })
+ this.eventSender('user-cancel', { eventType: 'page-redirect' })
+ }
+ })
+ .catch((err) => {
+ logger(2, err)
+ })
+ }
+
private async handleWalletRequired(authRequest) {
if (this.ui) {
this.ui.dismissLoader()
@@ -1083,6 +1132,7 @@ export class Client {
if (issuerConfig) this.storeOutletTokenResponse(res.data.tokens)
this.eventSender('tokens-selected', {
selectedTokens: this.tokenStore.getSelectedTokens(),
+ selectedIssuerKeys: Object.keys(this.tokenStore.getSelectedTokens()),
})
return res.data.tokens
}
@@ -1114,12 +1164,11 @@ export class Client {
}
}
+ // callback defined when hook added.
if (callback) {
- this.clientCallBackEvents[type] = callback
+ this.eventHookHandler.subscribe(type, callback)
} else {
- if (this.clientCallBackEvents[type]) {
- return this.clientCallBackEvents[type].call(type, data)
- }
+ this.eventHookHandler.trigger(type, data)
}
}
diff --git a/src/client/interface.ts b/src/client/interface.ts
index 5bb34a80..db6dd224 100644
--- a/src/client/interface.ts
+++ b/src/client/interface.ts
@@ -34,6 +34,7 @@ export interface OnChainTokenConfig extends IssuerConfigInterface {
openSeaSlug?: string
blockchain?: SupportedBlockchainsParam
oAuth2options?: any
+ abi?: string
}
export interface UltraIssuerConfig extends OnChainTokenConfig {
@@ -50,12 +51,7 @@ export interface SolanaIssuerConfig extends OnChainTokenConfig {
updateAuthority?: string
}
-export interface Oauth2IssuerConfig {
- collectionID: string
- onChain: boolean
- contract: string
- chain: string
- blockchain?: SupportedBlockchainsParam
+export interface Oauth2IssuerConfig extends OnChainTokenConfig {
oAuth2options: {
consumerKey: string
partnerTag: string
@@ -143,6 +139,7 @@ export interface MultiTokenInterface {
}
export interface AuthenticateInterface {
+ collectionId?: string
issuer: string
tokenId?: number | string
unsignedToken: any
@@ -159,15 +156,21 @@ export interface TokenNegotiatorEventsArgs {
'opened-overlay': EventSenderOpenedOverlay
'closed-overlay': EventSenderClosedOverlay
loaded: EventSenderViewLoaded
- 'token-proof': any // EventSenderTokenProof // TODO: Update this
+ 'token-proof': any
'connected-wallet': EventSenderConnectedWallet
'disconnected-wallet': EventSenderDisconnectedWallet
'tokens-selected': EventSenderTokensSelected
'tokens-loaded': EventSenderTokensLoaded
'network-change': string
+ 'page-redirecting': PageRedirecting
+ 'user-cancel': { eventType: string }
error: EventSenderError
}
+export interface PageRedirecting {
+ collectionId: string
+ tokenOrigin: string
+}
export interface EventSenderViewLoaded {
data: any
}
diff --git a/src/client/tokenStore.ts b/src/client/tokenStore.ts
index 42796a24..0adbcef5 100644
--- a/src/client/tokenStore.ts
+++ b/src/client/tokenStore.ts
@@ -1,4 +1,5 @@
-import { OffChainTokenConfig, OnChainIssuer, OnChainTokenConfig, SolanaIssuerConfig, UltraIssuerConfig } from './interface'
+import { OffChainTokenConfig, OnChainTokenConfig, SolanaIssuerConfig, UltraIssuerConfig } from './interface'
+import { LOCAL_STORAGE_TOKEN_STORE_KEY } from './../constants'
import { logger } from '../utils'
import { DecodedToken } from '../outlet/ticketStorage'
@@ -8,12 +9,13 @@ interface IssuerLookup {
}
interface TokenLookup {
- [issuer: string]: { timestamp: number; tokens: TokenData[] | null }
+ [issuer: string]: { loadAttempts: number; timestamp: number; tokens: TokenData[] | null }
}
export interface TokenData {
tokenId: string | number
walletAddress?: string
+ image?: string
// TODO: add more common fields to this interface
[key: string]: any
}
@@ -23,8 +25,6 @@ type TokenConfig = OnChainTokenConfig | OffChainTokenConfig | SolanaIssuerConfig
type SelectedTokens = { [collectionId: string]: { tokens: DecodedToken[] | TokenData[] } }
export class TokenStore {
- public static LOCAL_STORAGE_KEY = 'tn-tokenStore'
-
private currentIssuers: { [issuer: string]: boolean } = {} // mapping of issuer to on/off chain
private tokenData: TokenLookup = {}
@@ -42,11 +42,11 @@ export class TokenStore {
}
public clearTokenStore() {
- localStorage.removeItem(TokenStore.LOCAL_STORAGE_KEY)
+ localStorage.removeItem(LOCAL_STORAGE_TOKEN_STORE_KEY)
}
private loadTokenStore() {
- const tokenStoreData = JSON.parse(localStorage.getItem(TokenStore.LOCAL_STORAGE_KEY))
+ const tokenStoreData = JSON.parse(localStorage.getItem(LOCAL_STORAGE_TOKEN_STORE_KEY))
if (!tokenStoreData) return
@@ -59,7 +59,7 @@ export class TokenStore {
}
for (let collectionId in tokenStoreData.tokenData) {
- const tokenData = tokenStoreData.tokenData[collectionId] as { timestamp: number; tokens: [] }
+ const tokenData = tokenStoreData.tokenData[collectionId] as { timestamp: number; tokens: []; loadAttempts: number }
if (tokenData.timestamp + this.tokenPersistenceTTL * 1000 > Date.now()) {
this.tokenData[collectionId] = tokenData
@@ -72,7 +72,7 @@ export class TokenStore {
private saveTokenStore() {
if (this.tokenPersistenceTTL > 0)
localStorage.setItem(
- TokenStore.LOCAL_STORAGE_KEY,
+ LOCAL_STORAGE_TOKEN_STORE_KEY,
JSON.stringify({
tokenLookup: this.tokenLookup,
tokenData: this.tokenData,
@@ -182,11 +182,18 @@ export class TokenStore {
}
public setTokens(issuer: string, tokens: TokenData[] | DecodedToken[]) {
- this.tokenData[issuer] = { timestamp: Date.now(), tokens }
+ this.tokenData[issuer] = { timestamp: Date.now(), tokens, loadAttempts: this.getCollectionLoadAttempts(issuer) }
+ this.saveTokenStore()
+ if (this.autoEnableTokens) this.selectedTokens[issuer] = { tokens: tokens }
+ }
+ public setIncrementCollectionLoadAttempts(issuer: string) {
+ this.tokenData[issuer].loadAttempts = this.tokenData[issuer]?.loadAttempts >= 0 ? (this.tokenData[issuer].loadAttempts += 1) : 0
this.saveTokenStore()
+ }
- if (this.autoEnableTokens) this.selectedTokens[issuer] = { tokens: tokens }
+ public getCollectionLoadAttempts(issuer: string) {
+ return this.tokenData[issuer]?.loadAttempts ?? 0
}
public getSelectedTokens() {
diff --git a/src/client/ui.ts b/src/client/ui.ts
index a858e7e8..c4215a04 100644
--- a/src/client/ui.ts
+++ b/src/client/ui.ts
@@ -2,11 +2,10 @@ import { Start } from './views/start'
import { logger, requiredParams } from '../utils'
import { Client, ClientError } from './index'
-import { ViewInterface, ViewComponent, ViewFactory, ViewConstructor } from './views/view-interface'
-import { TokenStore } from './tokenStore'
+import { ViewInterface, ViewComponent, ViewFactory, ViewConstructor, AbstractView } from './views/view-interface'
import { SelectIssuers } from './views/select-issuers'
import { SelectWallet } from './views/select-wallet'
-
+import { LOCAL_STORAGE_TOKEN_STORE_KEY } from '../constants'
export type UIType = 'popup' | 'inline' // TODO: implement modal too
export type PopupPosition = 'bottom-right' | 'bottom-left' | 'top-left' | 'top-right'
export type UItheme = 'light' | 'dark'
@@ -25,10 +24,18 @@ export interface UIOptionsInterface {
openingAction?: string
issuerHeading?: string
repeatAction?: string
+ cancelAction?: string
+ walletDidntConnectAction?: string
+ authenticationHeadingEvent?: string
+ authenticationBodyEvent?: string
+ reDirectIssuerEventHeading?: string
+ reDirectIssuerBodyEvent?: string
+
theme?: UItheme
position?: PopupPosition
autoPopup?: boolean
alwaysShowStartScreen?: boolean
+ userCancelIssuerAutoRedirectTimer?: number
viewOverrides?: {
[type: string]: {
component?: ViewComponent
@@ -148,13 +155,9 @@ export class Ui implements UiInterface {
}
public async getStartScreen() {
- if (
- this.options.alwaysShowStartScreen ||
- !localStorage.getItem(TokenStore.LOCAL_STORAGE_KEY) ||
- !this.client.getTokenStore().getTotalTokenCount()
- )
+ if (this.options.alwaysShowStartScreen || !localStorage.getItem(LOCAL_STORAGE_TOKEN_STORE_KEY)) {
return 'start'
-
+ }
if (await this.canSkipWalletSelection()) {
this.client.enrichTokenLookupDataOnChainTokens()
return 'main'
@@ -397,9 +400,7 @@ export class Ui implements UiInterface {
loader.style.display = 'block'
const dismissBtn = this.loadContainer.querySelector('.dismiss-error-tn') as HTMLDivElement
dismissBtn.style.display = 'none'
-
this.loadContainer.querySelector('.loader-msg-tn').innerHTML = message.join('\n')
-
this.loadContainer.style.display = 'flex'
}
diff --git a/src/client/views/select-issuers.ts b/src/client/views/select-issuers.ts
index 68e4cf29..f7772e49 100644
--- a/src/client/views/select-issuers.ts
+++ b/src/client/views/select-issuers.ts
@@ -4,7 +4,6 @@ import { IconView } from './icon-view'
import { logger } from '../../utils'
import { UIUpdateEventType } from '../index'
import { Issuer } from '../interface'
-import { applyHTMLElementsInnerText } from './utils/index'
export class SelectIssuers extends AbstractView {
issuerListContainer: any
@@ -18,7 +17,6 @@ export class SelectIssuers extends AbstractView {
this.client.registerUiUpdateCallback(UIUpdateEventType.ISSUERS_LOADED, () => {
this.ui.dismissLoader()
- this.client.cancelTokenAutoload()
this.render()
})
@@ -111,7 +109,9 @@ export class SelectIssuers extends AbstractView {
protected afterRender() {
if (this.client.issuersLoaded) {
- if (this.client.getTokenStore().hasUnloadedTokens()) this.autoLoadTokens()
+ if (this.client.getTokenStore().hasUnloadedTokens()) {
+ this.autoLoadTokens()
+ }
} else {
this.issuersLoading()
}
@@ -158,9 +158,10 @@ export class SelectIssuers extends AbstractView {
new IconView(elem, params).render()
}
- this.issuerListContainer.addEventListener('click', (e: any) => {
+ this.issuerListContainer.addEventListener('click', async (e: any) => {
if (e.target.classList.contains('connect-btn-tn')) {
- this.connectTokenIssuer(e)
+ await this.connectTokenIssuer(e)
+ this.client.getTokenStore().setIncrementCollectionLoadAttempts(e.target.dataset.issuer)
} else if (e.target.classList.contains('tokens-btn-tn')) {
const issuer = e.target.parentNode.dataset.issuer
this.navigateToTokensView(issuer)
@@ -170,10 +171,14 @@ export class SelectIssuers extends AbstractView {
issuerConnectMarkup(title: string, image: string | undefined, issuer: string, tokens: any[], data: Issuer) {
let buttonText = ''
-
+ const collectLoadAttempts = this.client.getTokenStore().getCollectionLoadAttempts(issuer)
+ let issuerButtonText = this.params.options?.loadAction ?? 'Load Collection'
+ if (collectLoadAttempts >= 1) issuerButtonText = this.params.options?.repeatAction ?? 'Retry'
// @ts-ignore
- if (tokens?.length) buttonText = data?.fungible ? this.params.options.balanceFoundEvent ?? 'Balance found' : `${tokens.length} ${this.params.options?.nftsFoundEvent ?? 'Token(s) Available'}`
-
+ if (tokens?.length)
+ buttonText = data?.fungible
+ ? this.params.options?.balanceFoundEvent ?? 'Balance found'
+ : `${tokens.length} ${this.params.options?.nftsFoundEvent ?? 'Token(s) Available'}`
return `
@@ -186,10 +191,11 @@ export class SelectIssuers extends AbstractView {
data-issuer="${issuer}"
${this.client.issuersLoaded === true ? '' : 'disabled'}
>
- ${this.client.issuersLoaded === true
- ? this.params.options.loadAction ?? 'Load'
- : '
'
- }
+ ${
+ this.client.issuersLoaded === true
+ ? issuerButtonText
+ : '
'
+ }
{
- if (!tokens?.length) {
- applyHTMLElementsInnerText(
- this.issuerListContainer,
- `[data-issuer="${issuer}"] .connect-btn-tn`,
- this.params.options.loadAction ?? 'Load',
- )
- return
- }
+ await this.client.tokenAutoLoad(this.issuerLoading.bind(this), this.issuerLoadingComplete.bind(this), refresh)
+ }
+
+ issuerLoadingComplete(issuer: string, tokens: any[]) {
+ if (!tokens?.length) {
+ this.issuerDidntConnect(issuer)
+ } else {
+ this.issuerConnected(issuer, tokens, false)
+ }
+ }
- this.issuerConnected(issuer, tokens, false)
- },
- refresh,
- )
+ issuerDidntConnect(issuer: string, showNotification = false) {
+ const collectLoadAttempts = this.client.getTokenStore().getCollectionLoadAttempts(issuer)
+ let issuerButtonText = this.params.options?.loadAction ?? 'Load Collection'
+ if (collectLoadAttempts >= 1) issuerButtonText = this.params.options?.repeatAction ?? 'Retry'
+ const connectBtn = this.issuerListContainer.querySelector(`[data-issuer="${issuer}"] .connect-btn-tn`)
+ if (connectBtn) {
+ connectBtn.innerHTML = issuerButtonText
+ connectBtn.style.display = 'block'
+ }
+ if (showNotification) {
+ this.ui.showError(`
+ ${this.params.options.noTokensFoundEvent ?? 'No tokens found! '}
+ ${this.client.getNoTokenMsg(issuer)}`)
+ }
}
async connectTokenIssuer(event: any) {
@@ -240,31 +255,18 @@ export class SelectIssuers extends AbstractView {
try {
tokens = await this.client.connectTokenIssuer(issuer)
-
if (!tokens) return // Site is redirecting
} catch (err) {
logger(2, err)
this.ui.showError(err)
this.client.eventSender('error', { issuer, error: err })
- applyHTMLElementsInnerText(
- this.issuerListContainer,
- `[data-issuer="${issuer}"] .connect-btn-tn`,
- this.params.options.repeatAction ?? 'Try Again',
- )
return
}
this.ui.dismissLoader()
if (!tokens?.length) {
- this.ui.showError(`
- ${this.params.options.noTokensFoundEvent ?? 'No tokens found! '}
- ${this.client.getNoTokenMsg(issuer)}`)
- applyHTMLElementsInnerText(
- this.issuerListContainer,
- `[data-issuer="${issuer}"] .connect-btn-tn`,
- this.params.options.repeatAction ?? 'Try Again',
- )
+ this.issuerDidntConnect(issuer, true)
return
}
@@ -299,7 +301,9 @@ export class SelectIssuers extends AbstractView {
let issuers = this.client.getTokenStore().getCurrentIssuers()
tokenBtn.innerHTML =
- tokens.length && issuers[issuer].fungible ? this.params.options.balanceFoundEvent ?? 'Balance found' : `${tokens.length} ${this.params.options?.nftsFoundEvent ?? 'Token(s) Available'}`
+ tokens.length && issuers[issuer].fungible
+ ? this.params.options.balanceFoundEvent ?? 'Balance found'
+ : `${tokens.length} ${this.params.options?.nftsFoundEvent ?? 'Token(s) Available'}`
tokenBtn.setAttribute('aria-label', `Navigate to select from ${tokens.length} of your ${issuer} tokens`)
tokenBtn.setAttribute('tabIndex', 1)
@@ -343,7 +347,7 @@ export class SelectIssuers extends AbstractView {
if (config.onChain === false) {
const { title, image } = config
- const tokenId = t.tokenId ?? t.ticketId ?? i.toString();
+ const tokenId = t.tokenId ?? t.ticketId ?? i.toString()
tokens.push({
data: t,
tokenIssuerKey: issuer,
diff --git a/src/client/views/utils/index.ts b/src/client/views/utils/index.ts
deleted file mode 100644
index d038a468..00000000
--- a/src/client/views/utils/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-export const applyHTMLElementsInnerText = (searchElment: HTMLElement, querySelectionString: string, text: string) => {
- const element: HTMLElement = searchElment.querySelector(querySelectionString)
- if (element) element.innerText = text
-}
\ No newline at end of file
diff --git a/src/client/views/view-interface.ts b/src/client/views/view-interface.ts
index 64456a30..13e77bea 100644
--- a/src/client/views/view-interface.ts
+++ b/src/client/views/view-interface.ts
@@ -2,7 +2,7 @@ import { Client } from '../index'
import { Ui, UIOptionsInterface } from '../ui'
export interface ViewConstructor {
- new (client: Client, popup: Ui, viewContainer: any, params: any): T
+ new(client: Client, popup: Ui, viewContainer: any, params: any): T
}
export type ViewFactory = (client: Client, popup: Ui, viewContainer: any, params: any) => ViewInterface
@@ -16,7 +16,7 @@ export interface ViewInterface {
params: IViewParameters
render(): void
init(): void
- update(params: IViewParameters): void
+ update?(params: IViewParameters): void
}
export interface IViewParameters {
@@ -40,7 +40,7 @@ export abstract class AbstractView implements ViewInterface {
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
- public init(): void {}
+ public init(): void { }
abstract render(): void
diff --git a/src/constants.ts b/src/constants.ts
new file mode 100644
index 00000000..6f5bf75b
--- /dev/null
+++ b/src/constants.ts
@@ -0,0 +1,28 @@
+export const HOLDING_KEY_ALGORITHM = 'RSASSA-PKCS1-v1_5'
+export const DEFAULT_UN_ENDPOINT = 'https://api.smarttokenlabs.com/un'
+export const COMMON_UN_API_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwcm9qZWN0IjoidG9rZW4tbmVnb3RpYXRvciIsImlhdCI6MTY4OTc1NzQ4Nn0.ELE1OVvVFY1yrWlbnxtQur6dgeVxmKlPb9LZ_8cMOs8'
+export const LOCAL_STORAGE_PROOF_KEY = 'tn-proof'
+export const LOCAL_STORAGE_TOKEN_STORE_KEY = 'tn-tokenStore'
+export const LOCAL_STORAGE_TOKEN_KEY = 'tn-tokens'
+export const LOCAL_STORAGE_WALLET_KEY = 'tn-wallet-connections'
+export const LOCAL_STORAGE_KEY_WHITELIST_KEY = 'tn-whitelist'
+export const LOCAL_STORAGE_ATTESTATION_KEY = 'tn-id-attestations'
+export const BASE_TOKEN_DISCOVERY_URL = 'https://api.token-discovery.tokenscript.org'
+export const SUPPORTED_EVM_BLOCKCHAINS = ['evm', 'polygon', 'optimism', 'bsc', 'avalanche', 'fantom', 'goerli', 'mumbai', 'arbitrum']
+export const DEFAULT_RPC_MAP = {
+ 1: 'https://ethereum.publicnode.com', // mainnet
+ 5: 'https://eth-goerli.g.alchemy.com/v2/yVhq9zPJorAWsw-F87fEabSUl7cCU6z4', // Goerli
+ 11155111: 'https://sepolia.infura.io/v3/9f79b2f9274344af90b8d4e244b580ef', // Sepolia
+ 137: 'https://polygon-rpc.com/', // Polygon
+ 80001: 'https://polygon-mumbai.g.alchemy.com/v2/rVI6pOV4irVsrw20cJxc1fxK_1cSeiY0', // mumbai
+ 56: 'https://bsc-dataseed.binance.org/', // BSC,
+ 97: 'https://data-seed-prebsc-1-s1.binance.org:8545', // BSC testnet
+ 43114: 'https://api.avax.network/ext/bc/C/rpc', // Avalanche
+ 43113: 'https://api.avax-test.network/ext/bc/C/rpc', // Fuji testnet
+ 250: 'https://rpc.fantom.network/', // Fantom,
+ 25: 'https://evm-cronos.crypto.org', // Cronos,
+ 338: 'https://evm-t3.cronos.org', // Cronos testnet
+ 42161: 'https://arb1.arbitrum.io/rpc', // Arbitrum
+ 421613: 'https://arb-goerli.g.alchemy.com/v2/nFrflomLgsQQL5NWjGileAVqIGGxZWce', // Arbitrum goerli,
+ 10: 'https://mainnet.optimism.io', // Optimism
+}
diff --git a/src/core/constants.ts b/src/core/constants.ts
deleted file mode 100644
index a531a44d..00000000
--- a/src/core/constants.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-export const DEFAULT_RPC_MAP = {
- 1: 'https://ethereum.publicnode.com', // mainnet
- 5: 'https://eth-goerli.g.alchemy.com/v2/yVhq9zPJorAWsw-F87fEabSUl7cCU6z4', // Goerli
- 11155111: 'https://sepolia.infura.io/v3/9f79b2f9274344af90b8d4e244b580ef', // Sepolia
- 137: 'https://polygon-rpc.com/', // Polygon
- 80001: 'https://polygon-mumbai.g.alchemy.com/v2/rVI6pOV4irVsrw20cJxc1fxK_1cSeiY0', // mumbai
- 56: 'https://bsc-dataseed.binance.org/', // BSC,
- 97: 'https://data-seed-prebsc-1-s1.binance.org:8545', // BSC testnet
- 43114: 'https://api.avax.network/ext/bc/C/rpc', // Avalanche
- 43113: 'https://api.avax-test.network/ext/bc/C/rpc', // Fuji testnet
- 250: 'https://rpc.fantom.network/', // Fantom,
- 25: 'https://evm-cronos.crypto.org', // Cronos,
- 338: 'https://evm-t3.cronos.org', // Cronos testnet
- 42161: 'https://arb1.arbitrum.io/rpc', // Arbitrum
- 421613: 'https://arb-goerli.g.alchemy.com/v2/nFrflomLgsQQL5NWjGileAVqIGGxZWce', // Arbitrum goerli,
- 10: 'https://mainnet.optimism.io', // Optimism
-}
diff --git a/src/index.ts b/src/index.ts
index 0f379180..575e839c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,6 @@
export { Client } from './client/index'
export { AbstractView, type ViewInterface } from './client/views/view-interface'
+export { Ui, UIOptionsInterface } from './client/ui'
export { Outlet } from './outlet/index'
export { Start } from './client/views/start'
export { SelectWallet } from './client/views/select-wallet'
diff --git a/src/outlet/attestationIdClient.ts b/src/outlet/attestationIdClient.ts
index 2041c58b..9fd52ef0 100644
--- a/src/outlet/attestationIdClient.ts
+++ b/src/outlet/attestationIdClient.ts
@@ -1,7 +1,8 @@
-import { logger, removeUrlSearchParams } from '../utils'
+import { logger } from '../utils'
import { isBrave } from '../utils/support/getBrowserData'
import { OutletAction } from '../client/messaging'
import { URLNS } from '../core/messaging'
+import { LOCAL_STORAGE_ATTESTATION_KEY } from '../constants'
interface PostMessageData {
force?: boolean
@@ -37,7 +38,6 @@ export class AttestationIdClient {
private interval = null
private rejectHandler: Function
- private LOCAL_STORAGE_KEY = 'tn-id-attestations'
private attestations: StoredIdAttestations
constructor(
@@ -45,7 +45,7 @@ export class AttestationIdClient {
private showIframeCallback?: () => void,
private redirectUrl?: false | string,
) {
- this.attestations = JSON.parse(localStorage.getItem(this.LOCAL_STORAGE_KEY)) ?? ({} as StoredIdAttestations)
+ this.attestations = JSON.parse(localStorage.getItem(LOCAL_STORAGE_ATTESTATION_KEY)) ?? ({} as StoredIdAttestations)
}
private getExistingAttestation(id: string, idType = 'email') {
@@ -66,7 +66,7 @@ export class AttestationIdClient {
private saveAttestation(attestation: IdAttestationRecord) {
this.attestations[attestation.identifierType + '/' + attestation.identifier] = attestation
- localStorage.setItem(this.LOCAL_STORAGE_KEY, JSON.stringify(this.attestations))
+ localStorage.setItem(LOCAL_STORAGE_ATTESTATION_KEY, JSON.stringify(this.attestations))
}
openAttestationApp() {
diff --git a/src/outlet/getUseToken.ts b/src/outlet/getUseToken.ts
index 8015a483..3cfe9f36 100644
--- a/src/outlet/getUseToken.ts
+++ b/src/outlet/getUseToken.ts
@@ -3,7 +3,7 @@ import { EasZkProof } from '@tokenscript/attestation/dist/eas/EasZkProof'
import { Authenticator } from '@tokenscript/attestation'
import { logger } from '../utils'
import { OutletIssuerInterface, ProofResult } from './interfaces'
-import { DEFAULT_RPC_MAP } from '../core/constants'
+import { DEFAULT_RPC_MAP } from '../constants'
import { EthRPCMap } from '../client/interface'
export const getUseToken = async (
diff --git a/src/outlet/index.ts b/src/outlet/index.ts
index aa67443e..d0a6a66a 100644
--- a/src/outlet/index.ts
+++ b/src/outlet/index.ts
@@ -1,12 +1,11 @@
import { createIssuerHashArray, IssuerHashMap, logger, removeUrlSearchParams, requiredParams } from '../utils'
import { ResponseActionBase, ResponseInterfaceBase, URLNS } from '../core/messaging'
import { OutletAction, OutletResponseAction } from '../client/messaging'
-import { DecodedToken } from './ticketStorage'
import { Whitelist } from './whitelist'
import { LocalOutlet } from './localOutlet'
import { AttestationIdClient } from './attestationIdClient'
import { getUseToken } from './getUseToken'
-import { MultiTokenAuthRequest, MultiTokenAuthResult, OutletInterface, OutletIssuerInterface, ProofResult } from './interfaces'
+import { MultiTokenAuthRequest, OutletInterface, OutletIssuerInterface } from './interfaces'
export const defaultConfig = {
whitelistDialogWidth: '450px',
@@ -210,13 +209,13 @@ export class Outlet extends LocalOutlet {
async sendTokenProof(evtid: string) {
const collectionId: string = this.getDataFromQuery('issuer')
- const tokenId: string = this.getDataFromQuery('tokenId')
+ const token: string = this.getDataFromQuery('tn-token')
const wallet: string = this.getDataFromQuery('wallet')
const address: string = this.getDataFromQuery('address')
+ const tokenParsed = token ? JSON.parse(token) : undefined
+ const tokenId = tokenParsed.tokenId ?? this.getDataFromQuery('tokenId')
requiredParams(tokenId, 'tokenId is missing')
-
const redirect = this.getDataFromQuery('redirect') === 'true' ? window.location.href : false
-
try {
const issuer = this.getIssuerConfigById(collectionId)
diff --git a/src/outlet/localOutlet.ts b/src/outlet/localOutlet.ts
index 530608a2..2e190818 100644
--- a/src/outlet/localOutlet.ts
+++ b/src/outlet/localOutlet.ts
@@ -1,4 +1,4 @@
-import { DecodedToken, TicketStorage } from './ticketStorage'
+import { TicketStorage } from './ticketStorage'
import { IssuerHashMap, logger } from '../utils'
import { AttestationIdClient } from './attestationIdClient'
import { getUseToken } from './getUseToken'
diff --git a/src/outlet/ticketStorage.ts b/src/outlet/ticketStorage.ts
index f4878b48..430bb52a 100644
--- a/src/outlet/ticketStorage.ts
+++ b/src/outlet/ticketStorage.ts
@@ -1,13 +1,16 @@
-import { EasTicketAttestation, SchemaField, TicketSchema } from '@tokenscript/attestation/dist/eas/EasTicketAttestation'
+import {
+ EasTicketAttestation,
+ SchemaField,
+ SignedOffchainAttestation,
+ decodeBase64ZippedBase64,
+} from '@tokenscript/attestation/dist/eas/EasTicketAttestation'
import { KeyPair } from '@tokenscript/attestation/dist/libs/KeyPair'
import { base64ToUint8array, createIssuerHashArray, createOffChainCollectionHash, errorHandler, IssuerHashMap, logger } from '../utils'
import { Ticket } from '@tokenscript/attestation/dist/Ticket'
import { EasFieldDefinition, OutletInterface, OutletIssuerInterface } from './interfaces'
import { DevconTicket, SignedDevconTicket } from '@tokenscript/attestation/dist/asn1/shemas/SignedDevconTicket'
import { AsnParser } from '@peculiar/asn1-schema'
-import { decodeBase64ZippedBase64 } from '@ethereum-attestation-service/eas-sdk'
-import { SignedOffchainAttestation } from '@ethereum-attestation-service/eas-sdk/dist/offchain/offchain'
-import { DEFAULT_RPC_MAP } from '../core/constants'
+import { DEFAULT_RPC_MAP, LOCAL_STORAGE_TOKEN_KEY } from '../constants'
import { TokenStore } from '../client/tokenStore'
export type TokenType = 'asn' | 'eas'
@@ -50,6 +53,7 @@ export type DecodedToken = DecodedTokenData & {
type: TokenType
tokenId: string
signedToken: string
+ image?: string
}
export interface DecodedTokenData {
@@ -60,6 +64,7 @@ export interface DecodedTokenData {
commitment?: Uint8Array
easAttestation?: SignedOffchainAttestation
easData?: EasFieldData
+ image?: string
}
export interface EasFieldData {
@@ -100,8 +105,6 @@ export class TicketStorage {
private issuerHashConfigIndex?: { [hash: string]: OutletIssuerInterface }
- private static LOCAL_STORAGE_KEY = 'tn-tokens'
-
private signingKeys: { [eventId: string]: KeyPair[] } = {}
constructor(private config: OutletInterface) {
@@ -413,16 +416,15 @@ export class TicketStorage {
private loadTickets() {
try {
- if (!localStorage.getItem(TicketStorage.LOCAL_STORAGE_KEY)) return
-
- this.ticketCollections = JSON.parse(localStorage.getItem(TicketStorage.LOCAL_STORAGE_KEY)) as unknown as TicketStorageSchema
+ if (!localStorage.getItem(LOCAL_STORAGE_TOKEN_KEY)) return
+ this.ticketCollections = JSON.parse(localStorage.getItem(LOCAL_STORAGE_TOKEN_KEY)) as unknown as TicketStorageSchema
} catch (e) {
this.ticketCollections = {}
}
}
private storeTickets() {
- localStorage.setItem(TicketStorage.LOCAL_STORAGE_KEY, JSON.stringify(this.ticketCollections))
+ localStorage.setItem(LOCAL_STORAGE_TOKEN_KEY, JSON.stringify(this.ticketCollections))
}
public migrateLegacyTokenStorage(tokenStorageKey = 'dcTokens') {
diff --git a/src/outlet/whitelist.ts b/src/outlet/whitelist.ts
index eee0fe10..74f7dcac 100644
--- a/src/outlet/whitelist.ts
+++ b/src/outlet/whitelist.ts
@@ -1,6 +1,6 @@
-import { logger } from '../utils'
import { OutletInterface, OutletIssuerInterface } from './interfaces'
-import { createCookie, isCookieExpired } from '../utils'
+import { logger } from '../utils'
+import { LOCAL_STORAGE_KEY_WHITELIST_KEY } from '../constants'
export interface StoredWhitelist {
[origin: string]: {
@@ -13,8 +13,6 @@ export interface StaticWhitelist {
}
export class Whitelist {
- private static STORAGE_KEY = 'tn-whitelist'
-
private storedWhitelist: StoredWhitelist = {}
private staticWhitelist: StaticWhitelist = {}
@@ -35,12 +33,12 @@ export class Whitelist {
if (whitelist)
for (let origin of whitelist) {
try {
- origin = new URL(origin).origin
- if (!this.staticWhitelist[origin]) {
- this.staticWhitelist[origin] = [collectionID]
+ let _origin = new URL(origin).origin
+ if (!this.staticWhitelist[_origin]) {
+ this.staticWhitelist[_origin] = [collectionID]
continue
}
- this.staticWhitelist[origin].push(collectionID)
+ this.staticWhitelist[_origin].push(collectionID)
} catch (e) {
logger(2, 'Failed to validate whitelist origin: ' + e.message)
}
@@ -50,14 +48,14 @@ export class Whitelist {
private loadStoredWhitelist() {
try {
- this.storedWhitelist = JSON.parse(localStorage.getItem(Whitelist.STORAGE_KEY)) ?? ({} as StoredWhitelist)
+ this.storedWhitelist = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY_WHITELIST_KEY)) ?? ({} as StoredWhitelist)
} catch (e) {
// no-op
}
}
private saveWhitelist() {
- localStorage.setItem(Whitelist.STORAGE_KEY, JSON.stringify(this.storedWhitelist))
+ localStorage.setItem(LOCAL_STORAGE_KEY_WHITELIST_KEY, JSON.stringify(this.storedWhitelist))
}
public getWhitelistedIssuers(origin: string) {
@@ -99,7 +97,7 @@ export class Whitelist {
const collectionIds = issuers.map((issuer) => issuer.collectionID)
- if (force || (!this.isWhitelisted(origin, collectionIds) && isCookieExpired('tn-user-denied-access-to-connection'))) {
+ if (force || !this.isWhitelisted(origin, collectionIds)) {
await this.showWhitelistDialog(origin, issuers)
}
@@ -177,13 +175,6 @@ export class Whitelist {
const updateWhistList = () => {
if (this.storedWhitelist[origin]) delete this.storedWhitelist[origin]
this.saveWhitelist()
- // set for 10 seconds so the following send token requests don't re-open
- // the dialog multiple times when the user has declined connection.
- // adjust if needed to find the best timing, which should be the approx duration of
- // the time needed to ignore request for same site issuer dialog requests
- // and the time a user would come back to load the tokens again should
- // the decline by accident.
- createCookie('tn-user-denied-access-to-connection', true, 10)
}
document.getElementById('tn-access-deny').addEventListener('click', () => {
diff --git a/src/theme/common.scss b/src/theme/common.scss
index d922423f..0e5f6bcf 100644
--- a/src/theme/common.scss
+++ b/src/theme/common.scss
@@ -20,7 +20,8 @@
}
}
-.overlay-tn .cancel-auth-btn {
+.overlay-tn .cancel-auth-btn,
+.overlay-tn .cancel-autoload-btn {
display: block;
margin: 10px auto;
cursor: pointer;
@@ -93,19 +94,25 @@
height: auto;
max-height: 62px;
}
-
&.WalletConnectV2 {
svg {
width: 57px;
+ margin-top: -5px;
+ }
+ p {
+ padding-top: 10px;
}
}
-
&.Flow,
&.Phantom_Brave {
svg {
+ margin-top: 5px;
height: 52px;
width: auto;
}
+ p {
+ padding-top: 5px;
+ }
}
}
@@ -863,25 +870,6 @@ li.issuer-connect-banner-tn .fungible-token-btn {
display: flex;
flex-direction: column;
align-items: center;
-
- &.WalletConnectV2 {
- svg {
- margin-top: -5px;
- }
- p {
- padding-top: 10px;
- }
- }
-
- &.Flow,
- &.Phantom_Brave {
- svg {
- margin-top: 5px;
- }
- p {
- padding-top: 5px;
- }
- }
}
.overlay-tn .skeleton-box {
diff --git a/src/theme/theme_dark.scss b/src/theme/theme_dark.scss
index 782160dd..f39db144 100644
--- a/src/theme/theme_dark.scss
+++ b/src/theme/theme_dark.scss
@@ -58,7 +58,7 @@
.overlay-tn.dark-tn .overlay-content-tn {
box-shadow: 0 2px 4px 0 rgba(103, 103, 103, 0.5);
background-color: black;
- border-top: 4px solid white;
+ border-top: 4px solid #011aff;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 4px;
border-top-left-radius: 3px;
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 22308411..9b1e33d3 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -4,6 +4,8 @@ import { sha256 } from 'ethers/lib/utils'
import { OffChainTokenConfig } from '../client/interface'
import { OutletIssuerInterface } from '../outlet/interfaces'
import { DEFAULT_SCHEMA_UIDS } from '../outlet/ticketStorage'
+import { DecodedToken } from '../outlet/ticketStorage'
+import { TokenData } from '../client/tokenStore'
export interface IssuerHashMap {
[collectionId: string]: string[]
@@ -300,3 +302,7 @@ export const getCookieByName = (cookieName: string) => {
export const deleteCookieByName = (cookieName) => {
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`
}
+
+export const sleep = async (ms: number) => {
+ await new Promise((resolve) => setTimeout(resolve, ms))
+}
diff --git a/src/utils/support/isSupported.ts b/src/utils/support/isSupported.ts
index 4709d32a..046843c6 100644
--- a/src/utils/support/isSupported.ts
+++ b/src/utils/support/isSupported.ts
@@ -1,8 +1,7 @@
import { logger } from '..'
import { getBrowserData } from './getBrowserData'
import { errorHandler } from '../index'
-
-export const SUPPORTED_EVM_BLOCKCHAINS = ['evm', 'polygon', 'optimism', 'bsc', 'avalanche', 'fantom', 'goerli', 'mumbai', 'arbitrum']
+import { SUPPORTED_EVM_BLOCKCHAINS } from '../../constants';
export interface BrowserDataInterface {
iE?: boolean
@@ -26,7 +25,6 @@ export interface BrowserDataInterface {
status?: boolean
imToken?: boolean
brave?: boolean
-
metaMaskAndroid?: boolean
alphaWalletAndroid?: boolean
mewAndroid?: boolean
diff --git a/src/utils/token/fungibleTokenProvider.ts b/src/utils/token/fungibleTokenProvider.ts
index 94559c46..38519526 100644
--- a/src/utils/token/fungibleTokenProvider.ts
+++ b/src/utils/token/fungibleTokenProvider.ts
@@ -1,6 +1,6 @@
import { tokenRequest } from './../index'
import { OnChainIssuer } from '../../client/interface'
-import { BASE_TOKEN_DISCOVERY_URL } from './nftProvider'
+import { BASE_TOKEN_DISCOVERY_URL } from './../../constants'
export const getFungibleTokenBalances = async (issuer: OnChainIssuer, owner: string, ipfsBaseUrl?: string) => {
const { contract, chain } = issuer
@@ -14,12 +14,11 @@ export const getFungibleTokenBalances = async (issuer: OnChainIssuer, owner: str
}
export const getFungibleTokensMeta = async (issuer: OnChainIssuer, ipfsBaseUrl?: string) => {
-
+
const { contract, chain } = issuer
- let query = `${BASE_TOKEN_DISCOVERY_URL}/get-fungible-token?collectionAddress=${contract}&chain=${chain}&blockchain=${
- issuer.blockchain ?? 'evm'
- }`
+ let query = `${BASE_TOKEN_DISCOVERY_URL}/get-fungible-token?collectionAddress=${contract}&chain=${chain}&blockchain=${issuer.blockchain ?? 'evm'
+ }`
if (issuer.symbol) query += `&symbol=${issuer.symbol}`
if (ipfsBaseUrl) query += `&ipfsBaseUrl=${ipfsBaseUrl}`
return tokenRequest(query, true)
diff --git a/src/utils/token/nftProvider.ts b/src/utils/token/nftProvider.ts
index 1ca6241e..967a013d 100644
--- a/src/utils/token/nftProvider.ts
+++ b/src/utils/token/nftProvider.ts
@@ -1,13 +1,9 @@
import { tokenRequest } from './../index'
-
import { OnChainTokenConfig, SolanaIssuerConfig, OnChainIssuer, UltraIssuerConfig } from '../../client/interface'
import { validateBlockchain } from '../support/isSupported'
-
+import { BASE_TOKEN_DISCOVERY_URL } from './../../constants'
import { JsonRpc } from 'eosjs'
-// export const BASE_TOKEN_DISCOVERY_URL = 'http://localhost:3000'
-export const BASE_TOKEN_DISCOVERY_URL = 'https://api.token-discovery.tokenscript.org'
-
export const getNftCollection = async (issuer: OnChainIssuer, ipfsBaseUrl?: string) => {
let query: string
@@ -77,12 +73,12 @@ export const getNftTokens = async (issuer: OnChainIssuer, owner: string, ipfsBas
],
}
currentEndpoints = ultraEndpoints[issuer.chain]
- } catch(e){
+ } catch (e) {
throw new Error("Cant find endpoint")
}
-
+
let currentEndpointId = 0;
-
+
try {
// TODO add Ultra to the NFT monitoring service
@@ -99,26 +95,26 @@ export const getNftTokens = async (issuer: OnChainIssuer, owner: string, ipfsBas
lower_bound,
}
let is_catched
-
+
do {
- try{
+ try {
let currentEndpoint = currentEndpoints[currentEndpointId]
let rpc = new JsonRpc(currentEndpoint, { fetch })
-
+
result = await rpc.get_table_rows(params)
is_catched = false
- } catch (e){
+ } catch (e) {
is_catched = true
currentEndpointId++
}
- } while (is_catched && currentEndpointId < currentEndpoints.length)
-
+ } while (is_catched && currentEndpointId < currentEndpoints.length)
+
lower_bound = result.next_key
console.log(result.rows)
- if (result.rows.length){
+ if (result.rows.length) {
for (let i = 0; i < result.rows.length; i++) {
let row = result.rows[i]
- if (row.token_factory_id.toString() === (issuer as UltraIssuerConfig).factoryId.toString()){
+ if (row.token_factory_id.toString() === (issuer as UltraIssuerConfig).factoryId.toString()) {
nfts.push({
blockchain: "ultra",
collection: issuer.contract,
@@ -136,9 +132,9 @@ export const getNftTokens = async (issuer: OnChainIssuer, owner: string, ipfsBas
} while (result.more)
return nfts
} catch (e) {
- console.log( e )
+ console.log(e)
throw new Error("NFT read error")
- }
+ }
} else if ('blockchain' in issuer && issuer.blockchain === 'solana') {
query = getSolanaNftTokensUrl(issuer as SolanaIssuerConfig, owner, ipfsBaseUrl)
} else if ('blockchain' in issuer && issuer.blockchain === 'flow') {
diff --git a/src/version.ts b/src/version.ts
index 14c430da..124b6660 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1,2 +1,2 @@
// modified by build process.
-export const VERSION = '3.1.1'
+export const VERSION = '3.2.0'
diff --git a/src/wallet/Web3WalletProvider.ts b/src/wallet/Web3WalletProvider.ts
index 779c4369..e56dc4ef 100644
--- a/src/wallet/Web3WalletProvider.ts
+++ b/src/wallet/Web3WalletProvider.ts
@@ -3,7 +3,7 @@ import { logger, strToHexStr, strToUtfBytes, deleteCookieByName, isCookieMaxAgeE
import { SafeConnectOptions } from './SafeConnectProvider'
import { Client } from '../client'
import { SupportedBlockchainsParam, WalletOptionsInterface, SignatureSupportedBlockchainsParamList } from '../client/interface'
-import { DEFAULT_RPC_MAP } from '../core/constants'
+import { DEFAULT_RPC_MAP, LOCAL_STORAGE_WALLET_KEY } from '../constants'
import { Oauth2IssuerConfig } from './../client/interface'
interface WalletConnectionState {
@@ -50,8 +50,6 @@ export enum SupportedWalletProviders {
}
export class Web3WalletProvider {
- private static LOCAL_STORAGE_KEY = 'tn-wallet-connections'
-
connections: WalletConnectionState = {}
constructor(
@@ -72,7 +70,7 @@ export class Web3WalletProvider {
blockchain: con.blockchain,
}
}
- localStorage.setItem(Web3WalletProvider.LOCAL_STORAGE_KEY, JSON.stringify(savedConnections))
+ localStorage.setItem(LOCAL_STORAGE_WALLET_KEY, JSON.stringify(savedConnections))
}
emitSavedConnection(address: string): WalletConnection | WalletConnectionOauth2 | null {
@@ -95,7 +93,7 @@ export class Web3WalletProvider {
async deleteConnections() {
this.connections = {}
- let data = localStorage.getItem(Web3WalletProvider.LOCAL_STORAGE_KEY)
+ let data = localStorage.getItem(LOCAL_STORAGE_WALLET_KEY)
if (data) {
let state = JSON.parse(data)
if (state) {
@@ -130,12 +128,12 @@ export class Web3WalletProvider {
}
}
- localStorage.removeItem(Web3WalletProvider.LOCAL_STORAGE_KEY)
+ localStorage.removeItem(LOCAL_STORAGE_WALLET_KEY)
sessionStorage.removeItem('CURRENT_USER')
}
async loadConnections() {
- let data = localStorage.getItem(Web3WalletProvider.LOCAL_STORAGE_KEY)
+ let data = localStorage.getItem(LOCAL_STORAGE_WALLET_KEY)
if (!data) return
@@ -163,7 +161,7 @@ export class Web3WalletProvider {
// @ts-ignore
let address = await this[walletType as keyof Web3WalletProvider](checkConnectionOnly)
- if (!address) throw new Error("Wallet didn't connect")
+ if (!address) throw new Error(this.client.config.uiOptions?.walletDidntConnectAction ?? "Wallet didn't connect")
this.saveConnections()
this.emitSavedConnection(address)