Skip to content

Commit

Permalink
v4.10.0: adds metaplex resolver and local cache (24h)
Browse files Browse the repository at this point in the history
  • Loading branch information
10xSebastian committed May 17, 2023
1 parent d61079a commit d6b8a3c
Show file tree
Hide file tree
Showing 13 changed files with 586 additions and 85 deletions.
47 changes: 35 additions & 12 deletions dev.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<script crossorigin src="https://unpkg.com/@depay/solana-web3.js@1"></script>
<script crossorigin src="https://unpkg.com/@depay/web3-blockchains@8"></script>
<script crossorigin src="https://unpkg.com/@depay/web3-client@10"></script>
<script crossorigin src="https://unpkg.com/@depay/web3-tokens@9"></script>
<script src="tmp/index.dev.js"></script>
<link rel="stylesheet" href="https://unpkg.com/bootstrap@5/dist/css/bootstrap.css">
<style>
Expand Down Expand Up @@ -180,6 +181,28 @@ <h5 class="px-4 pb-4">Polygon Token from DePay API</h5>
</div>
</div>

<div class="d-block d-md-inline-block p-2 col-12 col-md-4">
<div class="px-0 py-4 rounded-lg bg-white border">
<div class="col px-0">
<div class="overflow-auto">
<h5 class="px-4 pb-4">Solana Token from Metaplex</h5>
<div class="px-4">
<div id="solanaTokenFromMetaplex" class="container d-flex justify-content-center token-image pt-2"></div>
</div>
</div>
<div class="px-4 pt-3">
<script>
ReactDOM.createRoot(
document.getElementById('solanaTokenFromMetaplex')
).render(
React.createElement(window.ReactTokenImage.TokenImage, { className:"Token", blockchain: 'solana', address: 'DePay1miDBPWXs6PVQrdC5Vch2jemgEPaiyXLNLLa2NF' }),
)
</script>
</div>
</div>
</div>
</div>

<div class="d-block d-md-inline-block p-2 col-12 col-md-4">
<div class="px-0 py-4 rounded-lg bg-white border">
<div class="col px-0">
Expand All @@ -206,17 +229,17 @@ <h5 class="px-4 pb-4">Solana Token from Trustwallet</h5>
<div class="px-0 py-4 rounded-lg bg-white border">
<div class="col px-0">
<div class="overflow-auto">
<h5 class="px-4 pb-4">Fantom Token</h5>
<h5 class="px-4 pb-4">Solana Token from DePay API</h5>
<div class="px-4">
<div id="fantomToken" class="container d-flex justify-content-center token-image pt-2"></div>
<div id="solanaTokenFromDepayApi" class="container d-flex justify-content-center token-image pt-2"></div>
</div>
</div>
<div class="px-4 pt-3">
<script>
ReactDOM.createRoot(
document.getElementById('fantomToken')
document.getElementById('solanaTokenFromDepayApi')
).render(
React.createElement(window.ReactTokenImage.TokenImage, { className:"Token", blockchain: 'fantom', address: '0x04068DA6C83AFCFA0e13ba15A6696662335D5B75' }),
React.createElement(window.ReactTokenImage.TokenImage, { className:"Token", blockchain: 'solana', address: 'epjfwdd5aufqssqem2qn1xzybapc8g4weggkzwytdt1v' })
)
</script>
</div>
Expand All @@ -228,17 +251,17 @@ <h5 class="px-4 pb-4">Fantom Token</h5>
<div class="px-0 py-4 rounded-lg bg-white border">
<div class="col px-0">
<div class="overflow-auto">
<h5 class="px-4 pb-4">Velas Token from Wagyu</h5>
<h5 class="px-4 pb-4">Fantom Token</h5>
<div class="px-4">
<div id="velasTokenFromWagyu" class="container d-flex justify-content-center token-image pt-2"></div>
<div id="fantomToken" class="container d-flex justify-content-center token-image pt-2"></div>
</div>
</div>
<div class="px-4 pt-3">
<script>
ReactDOM.createRoot(
document.getElementById('velasTokenFromWagyu')
document.getElementById('fantomToken')
).render(
React.createElement(window.ReactTokenImage.TokenImage, { className:"Token", blockchain: 'velas', address: '0xabf26902fd7b624e0db40d31171ea9dddf078351' }),
React.createElement(window.ReactTokenImage.TokenImage, { className:"Token", blockchain: 'fantom', address: '0x04068DA6C83AFCFA0e13ba15A6696662335D5B75' }),
)
</script>
</div>
Expand All @@ -250,17 +273,17 @@ <h5 class="px-4 pb-4">Velas Token from Wagyu</h5>
<div class="px-0 py-4 rounded-lg bg-white border">
<div class="col px-0">
<div class="overflow-auto">
<h5 class="px-4 pb-4">Solana Token from DePay API</h5>
<h5 class="px-4 pb-4">Velas Token from Wagyu</h5>
<div class="px-4">
<div id="solanaTokenFromDepayApi" class="container d-flex justify-content-center token-image pt-2"></div>
<div id="velasTokenFromWagyu" class="container d-flex justify-content-center token-image pt-2"></div>
</div>
</div>
<div class="px-4 pt-3">
<script>
ReactDOM.createRoot(
document.getElementById('solanaTokenFromDepayApi')
document.getElementById('velasTokenFromWagyu')
).render(
React.createElement(window.ReactTokenImage.TokenImage, { className:"Token", blockchain: 'solana', address: 'epjfwdd5aufqssqem2qn1xzybapc8g4weggkzwytdt1v' })
React.createElement(window.ReactTokenImage.TokenImage, { className:"Token", blockchain: 'velas', address: '0xabf26902fd7b624e0db40d31171ea9dddf078351' }),
)
</script>
</div>
Expand Down
80 changes: 73 additions & 7 deletions dist/esm/index.evm.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,98 @@
import { request } from '@depay/web3-client-evm';
import { Token } from '@depay/web3-tokens-evm';
import React, { useState, useEffect } from 'react';
import Blockchains from '@depay/web3-blockchains';

let supported = ['ethereum', 'bsc', 'polygon', 'fantom', 'velas'];
supported.evm = ['ethereum', 'bsc', 'polygon', 'fantom', 'velas'];
supported.solana = [];

const _jsxFileName = "/Users/sebastian/Work/DePay/react-token-image/src/index.js";

const _jsxFileName = "/Users/sebastian/Work/DePay/react-token-image/src/index.js"; function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }

const tokenURIAPI = [{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}];
const uriAPI = [{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}];
const UNKNOWN_IMAGE = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIKCSB2aWV3Qm94PSIwIDAgMjgzLjUgMjgzLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDI4My41IDI4My41OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxjaXJjbGUgZmlsbD0iI0YwRUZFRiIgY3g9IjE0MS43IiBjeT0iMTQxLjciIHI9IjE0MS43Ii8+CjxnPgoJPHBhdGggZmlsbD0iI0FCQUJBQiIgZD0iTTEyNywxNzUuMXYtNC40YzAtOC40LDEuMS0xNS4zLDMuNC0yMC43YzIuMy01LjQsNS4xLTEwLDguNC0xMy44YzMuMy0zLjcsNi42LTcsMTAuMS05LjdzNi4zLTUuNiw4LjYtOC41CgkJYzIuMy0yLjksMy40LTYuNCwzLjQtMTAuNWMwLTUtMS4xLTguNy0zLjMtMTEuMWMtMi4yLTIuNC01LTQtOC40LTQuOGMtMy40LTAuOC02LjktMS4zLTEwLjUtMS4zYy01LjgsMC0xMS44LDEtMTcuOSwyLjkKCQljLTYuMSwxLjktMTEuNSw0LjctMTYsOC40Vjc0YzIuMy0xLjcsNS40LTMuMyw5LjQtNC45YzQtMS42LDguNC0yLjksMTMuNC00YzUtMS4xLDEwLjEtMS42LDE1LjUtMS42YzguMSwwLDE1LjEsMS4xLDIxLjEsMy40CgkJYzYsMi4zLDEwLjgsNS41LDE0LjcsOS41YzMuOCw0LDYuNyw4LjcsOC42LDE0LjFjMS45LDUuMywyLjksMTEuMSwyLjksMTcuMmMwLDYuNi0xLjEsMTItMy40LDE2LjNjLTIuMyw0LjMtNS4xLDgtOC41LDExLjIKCQljLTMuNCwzLjItNi44LDYuNC0xMC4yLDkuNWMtMy40LDMuMS02LjMsNi44LTguNiwxMWMtMi4zLDQuMi0zLjQsOS41LTMuNCwxNS45djMuNEgxMjd6IE0xMjUuMiwyMTguMnYtMjcuN2gzM3YyNy43SDEyNS4yeiIvPgo8L2c+Cjwvc3ZnPgo=';

let TokenImage = function(props){

const [src, setSrc] = useState();
const [source, setSource] = useState('repository');
const [src, _setSrc] = useState();
const [source, setSource] = useState();

const blockchain = props.blockchain.toLowerCase();
const NATIVE = Blockchains.findByName(blockchain).currency.address;
const address = props.address;
const id = props.id;
const date = new Date();
const localStorageKey = ['react-token-image', blockchain, address, [date.getFullYear(), date.getMonth(), date.getDate()].join('-')].join('-');

const setSrc = (_src)=>{
localStorage.setItem(localStorageKey, _src);
_setSrc(_src);
};

useEffect(()=>{
const storedImage = localStorage.getItem(localStorageKey);
if(storedImage && storedImage.length) { return setSrc(storedImage) }
if(NATIVE.toLowerCase() == address.toLowerCase()) {
setSrc(Blockchains.findByName(blockchain).logo);
} else {
setSrc(logoFromRepository({ blockchain, address }));
if(supported.evm.includes(blockchain)) {
setSource('repository');
setSrc(logoFromRepository({ blockchain, address }));
} else if(blockchain === 'solana') {
setSource('metaplex');
logoFromMetaplex({ blockchain, address }).then((image)=>{
setSrc(image);
});
}
}
}, [blockchain, address]);

const logoFromMetaplex = ({ blockchain, address }) => {
return new Promise(async(resolve, reject)=>{
try {

let mintPublicKey = new PublicKey(address);
let metaDataPublicKey = new PublicKey(Token.solana.METADATA_ACCOUNT);

let seed = [
Buffer.from('metadata'),
metaDataPublicKey.toBuffer(),
mintPublicKey.toBuffer()
];

let tokenMetaDataPublicKey = (await PublicKey.findProgramAddress(seed, metaDataPublicKey))[0];

let metaData = await request({
blockchain,
address: tokenMetaDataPublicKey.toString(),
api: Token.solana.METADATA_LAYOUT,
cache: 86400000, // 1 day
});

if(_optionalChain([metaData, 'optionalAccess', _ => _.data, 'optionalAccess', _2 => _2.uri])) {

const uri = metaData.data.uri.replace(new RegExp('\u0000', 'g'), '');
if(uri && uri.length) {
await fetch(uri)
.then((response) => response.json())
.then((json)=>{
if(json && json.image) {
resolve(json.image);
} else {
resolve('');
}
}).catch(()=>resolve(''));
} else {
resolve('');
}
} else {
resolve('');
}

} catch (e) { resolve(''); }
})
};

const logoFromRepository = ({ blockchain, address })=> {
if(['ethereum', 'bsc', 'polygon', 'fantom', 'solana'].includes(blockchain)) {
Expand Down Expand Up @@ -86,7 +149,10 @@ let TokenImage = function(props){
};

const handleLoadError = (error)=> {
if(source == 'repository') {
if(source == 'metaplex') {
setSource('repository');
setSrc(logoFromRepository({ blockchain, address }));
} else if(source == 'repository') {
setSource('depay');
setSrc(`https://integrate.depay.com/tokens/${blockchain}/${address}/image`);
} else if (source == 'depay' && supported.evm.includes(blockchain)) {
Expand All @@ -109,7 +175,7 @@ let TokenImage = function(props){
React.createElement('img', {
className: props.className ,
src: src ,
onError: handleLoadError , __self: this, __source: {fileName: _jsxFileName, lineNumber: 112}}
onError: handleLoadError , __self: this, __source: {fileName: _jsxFileName, lineNumber: 178}}
)
)
};
Expand Down
81 changes: 74 additions & 7 deletions dist/esm/index.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,99 @@
import { PublicKey, Buffer } from '@depay/solana-web3.js';
import { request } from '@depay/web3-client';
import { Token } from '@depay/web3-tokens';
import React, { useState, useEffect } from 'react';
import Blockchains from '@depay/web3-blockchains';

let supported = ['ethereum', 'bsc', 'polygon', 'solana', 'fantom', 'velas'];
supported.evm = ['ethereum', 'bsc', 'polygon', 'fantom', 'velas'];
supported.solana = ['solana'];

const _jsxFileName = "/Users/sebastian/Work/DePay/react-token-image/src/index.js";

const _jsxFileName = "/Users/sebastian/Work/DePay/react-token-image/src/index.js"; function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }

const tokenURIAPI = [{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}];
const uriAPI = [{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}];
const UNKNOWN_IMAGE = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIKCSB2aWV3Qm94PSIwIDAgMjgzLjUgMjgzLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDI4My41IDI4My41OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxjaXJjbGUgZmlsbD0iI0YwRUZFRiIgY3g9IjE0MS43IiBjeT0iMTQxLjciIHI9IjE0MS43Ii8+CjxnPgoJPHBhdGggZmlsbD0iI0FCQUJBQiIgZD0iTTEyNywxNzUuMXYtNC40YzAtOC40LDEuMS0xNS4zLDMuNC0yMC43YzIuMy01LjQsNS4xLTEwLDguNC0xMy44YzMuMy0zLjcsNi42LTcsMTAuMS05LjdzNi4zLTUuNiw4LjYtOC41CgkJYzIuMy0yLjksMy40LTYuNCwzLjQtMTAuNWMwLTUtMS4xLTguNy0zLjMtMTEuMWMtMi4yLTIuNC01LTQtOC40LTQuOGMtMy40LTAuOC02LjktMS4zLTEwLjUtMS4zYy01LjgsMC0xMS44LDEtMTcuOSwyLjkKCQljLTYuMSwxLjktMTEuNSw0LjctMTYsOC40Vjc0YzIuMy0xLjcsNS40LTMuMyw5LjQtNC45YzQtMS42LDguNC0yLjksMTMuNC00YzUtMS4xLDEwLjEtMS42LDE1LjUtMS42YzguMSwwLDE1LjEsMS4xLDIxLjEsMy40CgkJYzYsMi4zLDEwLjgsNS41LDE0LjcsOS41YzMuOCw0LDYuNyw4LjcsOC42LDE0LjFjMS45LDUuMywyLjksMTEuMSwyLjksMTcuMmMwLDYuNi0xLjEsMTItMy40LDE2LjNjLTIuMyw0LjMtNS4xLDgtOC41LDExLjIKCQljLTMuNCwzLjItNi44LDYuNC0xMC4yLDkuNWMtMy40LDMuMS02LjMsNi44LTguNiwxMWMtMi4zLDQuMi0zLjQsOS41LTMuNCwxNS45djMuNEgxMjd6IE0xMjUuMiwyMTguMnYtMjcuN2gzM3YyNy43SDEyNS4yeiIvPgo8L2c+Cjwvc3ZnPgo=';

let TokenImage = function(props){

const [src, setSrc] = useState();
const [source, setSource] = useState('repository');
const [src, _setSrc] = useState();
const [source, setSource] = useState();

const blockchain = props.blockchain.toLowerCase();
const NATIVE = Blockchains.findByName(blockchain).currency.address;
const address = props.address;
const id = props.id;
const date = new Date();
const localStorageKey = ['react-token-image', blockchain, address, [date.getFullYear(), date.getMonth(), date.getDate()].join('-')].join('-');

const setSrc = (_src)=>{
localStorage.setItem(localStorageKey, _src);
_setSrc(_src);
};

useEffect(()=>{
const storedImage = localStorage.getItem(localStorageKey);
if(storedImage && storedImage.length) { return setSrc(storedImage) }
if(NATIVE.toLowerCase() == address.toLowerCase()) {
setSrc(Blockchains.findByName(blockchain).logo);
} else {
setSrc(logoFromRepository({ blockchain, address }));
if(supported.evm.includes(blockchain)) {
setSource('repository');
setSrc(logoFromRepository({ blockchain, address }));
} else if(blockchain === 'solana') {
setSource('metaplex');
logoFromMetaplex({ blockchain, address }).then((image)=>{
setSrc(image);
});
}
}
}, [blockchain, address]);

const logoFromMetaplex = ({ blockchain, address }) => {
return new Promise(async(resolve, reject)=>{
try {

let mintPublicKey = new PublicKey(address);
let metaDataPublicKey = new PublicKey(Token.solana.METADATA_ACCOUNT);

let seed = [
Buffer.from('metadata'),
metaDataPublicKey.toBuffer(),
mintPublicKey.toBuffer()
];

let tokenMetaDataPublicKey = (await PublicKey.findProgramAddress(seed, metaDataPublicKey))[0];

let metaData = await request({
blockchain,
address: tokenMetaDataPublicKey.toString(),
api: Token.solana.METADATA_LAYOUT,
cache: 86400000, // 1 day
});

if(_optionalChain([metaData, 'optionalAccess', _ => _.data, 'optionalAccess', _2 => _2.uri])) {

const uri = metaData.data.uri.replace(new RegExp('\u0000', 'g'), '');
if(uri && uri.length) {
await fetch(uri)
.then((response) => response.json())
.then((json)=>{
if(json && json.image) {
resolve(json.image);
} else {
resolve('');
}
}).catch(()=>resolve(''));
} else {
resolve('');
}
} else {
resolve('');
}

} catch (e) { resolve(''); }
})
};

const logoFromRepository = ({ blockchain, address })=> {
if(['ethereum', 'bsc', 'polygon', 'fantom', 'solana'].includes(blockchain)) {
Expand Down Expand Up @@ -86,7 +150,10 @@ let TokenImage = function(props){
};

const handleLoadError = (error)=> {
if(source == 'repository') {
if(source == 'metaplex') {
setSource('repository');
setSrc(logoFromRepository({ blockchain, address }));
} else if(source == 'repository') {
setSource('depay');
setSrc(`https://integrate.depay.com/tokens/${blockchain}/${address}/image`);
} else if (source == 'depay' && supported.evm.includes(blockchain)) {
Expand All @@ -109,7 +176,7 @@ let TokenImage = function(props){
React.createElement('img', {
className: props.className ,
src: src ,
onError: handleLoadError , __self: this, __source: {fileName: _jsxFileName, lineNumber: 112}}
onError: handleLoadError , __self: this, __source: {fileName: _jsxFileName, lineNumber: 179}}
)
)
};
Expand Down
Loading

0 comments on commit d6b8a3c

Please sign in to comment.