@collabland/chain
Advanced tools
Comparing version 0.2.6 to 0.3.0
import { Amount, AssetRequest, AssetResponse, Chain, ChainConnector, ChainType, NativeCurrency, TokenAsset, TokenContract, TokenId, TokenMetadata } from './types'; | ||
/** | ||
* Base chain connector | ||
* | ||
* @typeParam AMT - Amount type | ||
* @typeParam TID - Token id type | ||
* @typeParam M - Token metadata type | ||
*/ | ||
export declare abstract class BaseChainConnector<AMT = Amount, TID = TokenId, M = TokenMetadata> implements ChainConnector<AMT, TID, M> { | ||
abstract chainType: ChainType; | ||
/** | ||
* Native currency of the chain | ||
*/ | ||
abstract nativeCurrency: NativeCurrency; | ||
/** | ||
* Check if the token spec is for NFT | ||
* @param tokenSpec - Token spec | ||
*/ | ||
abstract isNFT(tokenSpec: string): boolean; | ||
/** | ||
* Check if the token spec is for FT | ||
* @param tokenSpec - Token spec | ||
*/ | ||
abstract isFT(tokenSpec: string): boolean; | ||
abstract getAmount(val: number | string): AMT; | ||
/** | ||
* Convert the value to `AMT` | ||
* @param val - Value | ||
*/ | ||
abstract toAmount(val: number | string | AMT): AMT; | ||
/** | ||
* Convert the token id to `TID` | ||
* @param id | ||
*/ | ||
abstract toTokenId(id: number | string | TID): TID; | ||
protected concurrency: number; | ||
@@ -12,5 +39,5 @@ getBalance(chain: Chain, account: string): Promise<AMT>; | ||
getNFTBalance(chain: Chain, account: string, nftContract: TokenContract, tokenType?: TID): Promise<TokenAsset<TID, M, AMT>[]>; | ||
getBalances(chain: Chain, account: string, ...requests: AssetRequest<TID>[]): Promise<AssetResponse<AMT, TID, M>[]>; | ||
getBalances(chain: Chain, account: string, ...requests: AssetRequest<TID | string | number>[]): Promise<AssetResponse<AMT, TID, M>[]>; | ||
getAssetTypes(chain: Chain, account: string): Promise<AssetRequest<TID>[]>; | ||
getAssets(chain: Chain, account: string): Promise<AssetResponse<AMT, TID, M>[]>; | ||
} |
@@ -10,2 +10,9 @@ "use strict"; | ||
const types_1 = require("./types"); | ||
/** | ||
* Base chain connector | ||
* | ||
* @typeParam AMT - Amount type | ||
* @typeParam TID - Token id type | ||
* @typeParam M - Token metadata type | ||
*/ | ||
class BaseChainConnector { | ||
@@ -25,3 +32,8 @@ constructor() { | ||
async getBalances(chain, account, ...requests) { | ||
const assets = common_1.pMap(requests, async (req) => { | ||
const assetRequests = requests.map(r => ({ | ||
address: r.address, | ||
tokenSpec: r.tokenSpec, | ||
tokenType: r.tokenType != null ? this.toTokenId(r.tokenType) : undefined, | ||
})); | ||
const assets = common_1.pMap(assetRequests, async (req) => { | ||
let asset; | ||
@@ -42,3 +54,3 @@ if (req.address === '' || | ||
const nfts = await this.getNFTBalance(chain, account, req, req.tokenType); | ||
asset = { balance: this.getAmount(nfts.length), tokens: nfts, ...req }; | ||
asset = { balance: this.toAmount(nfts.length), tokens: nfts, ...req }; | ||
return asset; | ||
@@ -45,0 +57,0 @@ } |
@@ -15,9 +15,69 @@ /** | ||
export interface Chain { | ||
type: ChainType; | ||
/** | ||
* Block chain type | ||
*/ | ||
type: ChainType | string; | ||
/** | ||
* Chain id | ||
*/ | ||
chainId?: string; | ||
/** | ||
* Chain network | ||
*/ | ||
network: string; | ||
/** | ||
* RPC URL | ||
*/ | ||
rpcUrl?: string; | ||
/** | ||
* Indexer URL | ||
*/ | ||
indexerUrl?: string; | ||
} | ||
/** | ||
* A blockchain resource | ||
*/ | ||
export interface ChainResource extends Chain { | ||
/** | ||
* Extra parameters | ||
*/ | ||
params?: Record<string, string>; | ||
/** | ||
* The resource path | ||
*/ | ||
path?: string; | ||
/** | ||
* Account address or name | ||
*/ | ||
account?: string; | ||
/** | ||
* Token contract | ||
*/ | ||
contract?: TokenContract; | ||
/** | ||
* Token id | ||
*/ | ||
tokenId?: string; | ||
} | ||
/** | ||
* Get the uri for a given chain resource in the form of | ||
* - `evm:mainnet/<account>` | ||
* - `evm:mainnet/erc20/<erc20-contract-address>` | ||
* - `evm:mainnet/erc721/<erc721-contract-address>/<token-id>` | ||
* | ||
* Inspired by https://github.com/ChainAgnostic/CAIPs | ||
* | ||
* @param chainResource - Chain resource | ||
* @returns | ||
*/ | ||
export declare function getChainUri(chainResource: ChainResource): string; | ||
/** | ||
* Parse the chain resource URI into an object | ||
* - `evm:mainnet?rpcUrl=...` | ||
* - `evm:1` | ||
* - `near:testnet` | ||
* @param uri - Chain resource uri | ||
*/ | ||
export declare function parseChainUri(uri: string): ChainResource; | ||
/** | ||
* Default type for amount | ||
@@ -142,3 +202,3 @@ */ | ||
*/ | ||
getBalances(chain: Chain, account: string, ...requests: AssetRequest<TID>[]): Promise<AssetResponse<AMT, TID, M>[]>; | ||
getBalances(chain: Chain, account: string, ...requests: AssetRequest<TID | string | number>[]): Promise<AssetResponse<AMT, TID, M>[]>; | ||
/** | ||
@@ -145,0 +205,0 @@ * Get balances for the given account on the chain |
@@ -7,3 +7,5 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.NATIVE_TOKEN = exports.ChainType = void 0; | ||
exports.NATIVE_TOKEN = exports.parseChainUri = exports.getChainUri = exports.ChainType = void 0; | ||
const common_1 = require("@collabland/common"); | ||
const debug = common_1.debugFactory('collabland:chain'); | ||
/** | ||
@@ -20,2 +22,102 @@ * Chain type | ||
})(ChainType = exports.ChainType || (exports.ChainType = {})); | ||
/** | ||
* Get the uri for a given chain resource in the form of | ||
* - `evm:mainnet/<account>` | ||
* - `evm:mainnet/erc20/<erc20-contract-address>` | ||
* - `evm:mainnet/erc721/<erc721-contract-address>/<token-id>` | ||
* | ||
* Inspired by https://github.com/ChainAgnostic/CAIPs | ||
* | ||
* @param chainResource - Chain resource | ||
* @returns | ||
*/ | ||
function getChainUri(chainResource) { | ||
var _a, _b, _c, _d, _e; | ||
const protocol = chainResource.type; | ||
const path = [ | ||
(_a = chainResource.chainId) !== null && _a !== void 0 ? _a : chainResource.network, | ||
(_c = (_b = chainResource.path) !== null && _b !== void 0 ? _b : chainResource.account) !== null && _c !== void 0 ? _c : (chainResource.contract | ||
? `${chainResource.contract.tokenSpec.toLowerCase()}:${chainResource.contract.address}` | ||
: ''), | ||
(_d = chainResource.tokenId) !== null && _d !== void 0 ? _d : '', | ||
] | ||
.filter(p => !!p) | ||
.join('/'); | ||
const params = (_e = chainResource.params) !== null && _e !== void 0 ? _e : {}; | ||
if (chainResource.rpcUrl) { | ||
params.rpcUrl = chainResource.rpcUrl; | ||
} | ||
if (chainResource.indexerUrl) { | ||
params.indexerUrl = chainResource.indexerUrl; | ||
} | ||
let query = Object.entries(params) | ||
.map(p => `${p[0]}=${p[1]}`) | ||
.join('&'); | ||
query = query ? `?${query}` : ''; | ||
return `${protocol}:${path}${query}`; | ||
} | ||
exports.getChainUri = getChainUri; | ||
/** | ||
* Parse the chain resource URI into an object | ||
* - `evm:mainnet?rpcUrl=...` | ||
* - `evm:1` | ||
* - `near:testnet` | ||
* @param uri - Chain resource uri | ||
*/ | ||
function parseChainUri(uri) { | ||
debug('Parsing chain uri: %s', uri); | ||
const url = new URL(uri); | ||
// The protocol is `evm:` or `near:` | ||
const type = url.protocol.replace(/\:$/, ''); | ||
let parts = (url.hostname + url.pathname).split('/').filter(p => p !== ''); | ||
let network = parts[0]; | ||
let chainId = undefined; | ||
if (!isNaN(parseInt(network))) { | ||
chainId = network; | ||
network = ''; | ||
} | ||
const query = {}; | ||
url.searchParams.forEach((val, key) => { | ||
query[key] = val; | ||
}); | ||
parts = parts.slice(1); | ||
const chainResource = { | ||
type, | ||
network, | ||
chainId, | ||
path: parts.join('/'), | ||
params: query, | ||
}; | ||
const rpcUrl = query.rpcUrl; | ||
if (rpcUrl != null) { | ||
chainResource.rpcUrl = rpcUrl; | ||
delete query.rpcUrl; | ||
} | ||
const indexerUrl = query.indexerUrl; | ||
if (indexerUrl != null) { | ||
chainResource.indexerUrl = indexerUrl; | ||
delete query.indexerUrl; | ||
} | ||
if (chainId == null) { | ||
delete chainResource.chainId; | ||
} | ||
if (parts.length) { | ||
if (parts[0].includes(':')) { | ||
const [tokenSpec, address] = parts[0].split(':'); | ||
chainResource.contract = { | ||
address, | ||
tokenSpec: tokenSpec.toUpperCase(), | ||
}; | ||
if (parts[1] != null) { | ||
chainResource.tokenId = parts[1]; | ||
} | ||
} | ||
else { | ||
chainResource.account = parts[0]; | ||
} | ||
} | ||
debug('Chain resource: %O', chainResource); | ||
return chainResource; | ||
} | ||
exports.parseChainUri = parseChainUri; | ||
exports.NATIVE_TOKEN = { | ||
@@ -22,0 +124,0 @@ address: '', |
{ | ||
"name": "@collabland/chain", | ||
"version": "0.2.6", | ||
"version": "0.3.0", | ||
"description": "CollabLand Ethereum Integration", | ||
@@ -34,3 +34,3 @@ "main": "dist/index.js", | ||
"dependencies": { | ||
"@collabland/common": "^0.19.0", | ||
"@collabland/common": "^0.19.1", | ||
"tslib": "^2.0.0" | ||
@@ -48,3 +48,3 @@ }, | ||
"author": "Abridged, Inc.", | ||
"gitHead": "dfe21db5afbbf30e277790168914a23cdd54df3b" | ||
"gitHead": "e0944f19fecd734aa41d1522059cb7c48137a37d" | ||
} |
@@ -22,2 +22,9 @@ // Copyright Abridged, Inc. 2021. All Rights Reserved. | ||
/** | ||
* Base chain connector | ||
* | ||
* @typeParam AMT - Amount type | ||
* @typeParam TID - Token id type | ||
* @typeParam M - Token metadata type | ||
*/ | ||
export abstract class BaseChainConnector< | ||
@@ -30,7 +37,32 @@ AMT = Amount, | ||
abstract chainType: ChainType; | ||
/** | ||
* Native currency of the chain | ||
*/ | ||
abstract nativeCurrency: NativeCurrency; | ||
/** | ||
* Check if the token spec is for NFT | ||
* @param tokenSpec - Token spec | ||
*/ | ||
abstract isNFT(tokenSpec: string): boolean; | ||
/** | ||
* Check if the token spec is for FT | ||
* @param tokenSpec - Token spec | ||
*/ | ||
abstract isFT(tokenSpec: string): boolean; | ||
abstract getAmount(val: number | string): AMT; | ||
/** | ||
* Convert the value to `AMT` | ||
* @param val - Value | ||
*/ | ||
abstract toAmount(val: number | string | AMT): AMT; | ||
/** | ||
* Convert the token id to `TID` | ||
* @param id | ||
*/ | ||
abstract toTokenId(id: number | string | TID): TID; | ||
protected concurrency = 4; | ||
@@ -62,6 +94,11 @@ | ||
account: string, | ||
...requests: AssetRequest<TID>[] | ||
...requests: AssetRequest<TID | string | number>[] | ||
): Promise<AssetResponse<AMT, TID, M>[]> { | ||
const assetRequests: AssetRequest<TID>[] = requests.map(r => ({ | ||
address: r.address, | ||
tokenSpec: r.tokenSpec, | ||
tokenType: r.tokenType != null ? this.toTokenId(r.tokenType) : undefined, | ||
})); | ||
const assets = pMap( | ||
requests, | ||
assetRequests, | ||
async req => { | ||
@@ -88,3 +125,3 @@ let asset: AssetResponse<AMT, TID, M>; | ||
); | ||
asset = {balance: this.getAmount(nfts.length), tokens: nfts, ...req}; | ||
asset = {balance: this.toAmount(nfts.length), tokens: nfts, ...req}; | ||
return asset; | ||
@@ -91,0 +128,0 @@ } |
159
src/types.ts
@@ -6,2 +6,5 @@ // Copyright Abridged, Inc. 2021. All Rights Reserved. | ||
import {debugFactory} from '@collabland/common'; | ||
const debug = debugFactory('collabland:chain'); | ||
/** | ||
@@ -22,6 +25,25 @@ * Chain type | ||
export interface Chain { | ||
type: ChainType; | ||
/** | ||
* Block chain type | ||
*/ | ||
type: ChainType | string; | ||
/** | ||
* Chain id | ||
*/ | ||
chainId?: string; | ||
/** | ||
* Chain network | ||
*/ | ||
network: string; | ||
/** | ||
* RPC URL | ||
*/ | ||
rpcUrl?: string; | ||
/** | ||
* Indexer URL | ||
*/ | ||
indexerUrl?: string; | ||
@@ -31,2 +53,135 @@ } | ||
/** | ||
* A blockchain resource | ||
*/ | ||
export interface ChainResource extends Chain { | ||
/** | ||
* Extra parameters | ||
*/ | ||
params?: Record<string, string>; | ||
/** | ||
* The resource path | ||
*/ | ||
path?: string; | ||
/** | ||
* Account address or name | ||
*/ | ||
account?: string; | ||
/** | ||
* Token contract | ||
*/ | ||
contract?: TokenContract; | ||
/** | ||
* Token id | ||
*/ | ||
tokenId?: string; | ||
} | ||
/** | ||
* Get the uri for a given chain resource in the form of | ||
* - `evm:mainnet/<account>` | ||
* - `evm:mainnet/erc20/<erc20-contract-address>` | ||
* - `evm:mainnet/erc721/<erc721-contract-address>/<token-id>` | ||
* | ||
* Inspired by https://github.com/ChainAgnostic/CAIPs | ||
* | ||
* @param chainResource - Chain resource | ||
* @returns | ||
*/ | ||
export function getChainUri(chainResource: ChainResource) { | ||
const protocol = chainResource.type; | ||
const path = [ | ||
chainResource.chainId ?? chainResource.network, | ||
chainResource.path ?? | ||
chainResource.account ?? | ||
(chainResource.contract | ||
? `${chainResource.contract.tokenSpec.toLowerCase()}:${ | ||
chainResource.contract.address | ||
}` | ||
: ''), | ||
chainResource.tokenId ?? '', | ||
] | ||
.filter(p => !!p) | ||
.join('/'); | ||
const params = chainResource.params ?? {}; | ||
if (chainResource.rpcUrl) { | ||
params.rpcUrl = chainResource.rpcUrl; | ||
} | ||
if (chainResource.indexerUrl) { | ||
params.indexerUrl = chainResource.indexerUrl; | ||
} | ||
let query = Object.entries(params) | ||
.map(p => `${p[0]}=${p[1]}`) | ||
.join('&'); | ||
query = query ? `?${query}` : ''; | ||
return `${protocol}:${path}${query}`; | ||
} | ||
/** | ||
* Parse the chain resource URI into an object | ||
* - `evm:mainnet?rpcUrl=...` | ||
* - `evm:1` | ||
* - `near:testnet` | ||
* @param uri - Chain resource uri | ||
*/ | ||
export function parseChainUri(uri: string): ChainResource { | ||
debug('Parsing chain uri: %s', uri); | ||
const url = new URL(uri); | ||
// The protocol is `evm:` or `near:` | ||
const type = url.protocol.replace(/\:$/, '') as ChainType; | ||
let parts = (url.hostname + url.pathname).split('/').filter(p => p !== ''); | ||
let network = parts[0]; | ||
let chainId = undefined; | ||
if (!isNaN(parseInt(network))) { | ||
chainId = network; | ||
network = ''; | ||
} | ||
const query: Record<string, string> = {}; | ||
url.searchParams.forEach((val, key) => { | ||
query[key] = val; | ||
}); | ||
parts = parts.slice(1); | ||
const chainResource: ChainResource = { | ||
type, | ||
network, | ||
chainId, | ||
path: parts.join('/'), | ||
params: query, | ||
}; | ||
const rpcUrl = query.rpcUrl; | ||
if (rpcUrl != null) { | ||
chainResource.rpcUrl = rpcUrl; | ||
delete query.rpcUrl; | ||
} | ||
const indexerUrl = query.indexerUrl; | ||
if (indexerUrl != null) { | ||
chainResource.indexerUrl = indexerUrl; | ||
delete query.indexerUrl; | ||
} | ||
if (chainId == null) { | ||
delete chainResource.chainId; | ||
} | ||
if (parts.length) { | ||
if (parts[0].includes(':')) { | ||
const [tokenSpec, address] = parts[0].split(':'); | ||
chainResource.contract = { | ||
address, | ||
tokenSpec: tokenSpec.toUpperCase(), | ||
}; | ||
if (parts[1] != null) { | ||
chainResource.tokenId = parts[1]; | ||
} | ||
} else { | ||
chainResource.account = parts[0]; | ||
} | ||
} | ||
debug('Chain resource: %O', chainResource); | ||
return chainResource; | ||
} | ||
/** | ||
* Default type for amount | ||
@@ -187,3 +342,3 @@ */ | ||
account: string, | ||
...requests: AssetRequest<TID>[] | ||
...requests: AssetRequest<TID | string | number>[] | ||
): Promise<AssetResponse<AMT, TID, M>[]>; | ||
@@ -190,0 +345,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
226708
7639
+ Addedcross-fetch@3.2.0(transitive)
- Removedcross-fetch@3.1.8(transitive)
Updated@collabland/common@^0.19.1