@mimicprotocol/lib-ts 0.0.1-rc.9 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +134 -0
- package/README.md +7 -7
- package/constants.d.ts +1 -0
- package/constants.js +1 -0
- package/index.ts +3 -0
- package/package.json +18 -4
- package/src/chains/Arbitrum.ts +14 -0
- package/src/chains/Avalanche.ts +15 -0
- package/src/chains/BNB.ts +15 -0
- package/src/chains/Base.ts +14 -0
- package/src/chains/BaseSepolia.ts +7 -0
- package/src/chains/Ethereum.ts +7 -7
- package/src/chains/Gnosis.ts +14 -0
- package/src/chains/Optimism.ts +7 -7
- package/src/chains/Polygon.ts +10 -7
- package/src/chains/Sonic.ts +13 -0
- package/src/chains/index.ts +7 -0
- package/src/context/Context.ts +102 -7
- package/src/environment.ts +165 -53
- package/src/evm.ts +5 -4
- package/src/helpers/BorshDeserializer.ts +133 -0
- package/src/helpers/consensus.ts +35 -0
- package/src/helpers/constants.ts +7 -1
- package/src/helpers/index.ts +5 -0
- package/src/helpers/math.ts +20 -0
- package/src/helpers/serialize.ts +5 -125
- package/src/helpers/strings.ts +82 -5
- package/src/intents/Call/EvmCall.ts +283 -0
- package/src/intents/Call/SvmCall.ts +278 -0
- package/src/intents/Call/index.ts +2 -0
- package/src/intents/Intent.ts +178 -5
- package/src/intents/Swap.ts +136 -44
- package/src/intents/Transfer.ts +103 -58
- package/src/log.ts +83 -0
- package/src/queries/EvmCallQuery.ts +43 -0
- package/src/queries/QueryResponse.ts +26 -0
- package/src/queries/RelevantTokensQuery.ts +82 -0
- package/src/queries/SubgraphQuery.ts +50 -0
- package/src/queries/SvmAccountsInfoQuery.ts +65 -0
- package/src/queries/TokenPriceQuery.ts +47 -0
- package/src/queries/index.ts +6 -1
- package/src/storage/index.ts +1 -0
- package/src/storage/storage.ts +46 -0
- package/src/svm.ts +27 -0
- package/src/tokens/BlockchainToken.ts +108 -0
- package/src/tokens/DenominationToken.ts +70 -0
- package/src/tokens/ERC20Token.ts +192 -0
- package/src/tokens/SPLToken.ts +162 -0
- package/src/tokens/Token.ts +55 -155
- package/src/tokens/TokenAmount.ts +72 -30
- package/src/tokens/TokenProvider.ts +54 -0
- package/src/tokens/Tokens.ts +186 -0
- package/src/tokens/USD.ts +9 -6
- package/src/tokens/index.ts +6 -0
- package/src/types/Address.ts +86 -14
- package/src/types/BigInt.ts +14 -22
- package/src/types/ByteArray.ts +41 -3
- package/src/types/Bytes.ts +7 -0
- package/src/types/ChainId.ts +9 -1
- package/src/types/Option.ts +35 -0
- package/src/types/Result.ts +68 -0
- package/src/types/TriggerType.ts +4 -0
- package/src/types/evm/EvmDecodeParam.ts +7 -0
- package/src/types/evm/EvmEncodeParam.ts +31 -0
- package/src/types/evm/index.ts +2 -0
- package/src/types/index.ts +8 -2
- package/src/types/svm/SvmAccountInfo.ts +32 -0
- package/src/types/svm/SvmAccountMeta.ts +28 -0
- package/src/types/svm/SvmFindProgramAddress.ts +32 -0
- package/src/types/svm/SvmMint.ts +44 -0
- package/src/types/svm/SvmPdaSeed.ts +19 -0
- package/src/types/svm/SvmTokenMetadataData.ts +29 -0
- package/src/types/svm/index.ts +5 -0
- package/src/intents/Call.ts +0 -238
- package/src/queries/Call.ts +0 -16
- package/src/types/EvmDecodeParam.ts +0 -30
- package/src/types/EvmEncodeParam.ts +0 -54
package/src/environment.ts
CHANGED
|
@@ -1,14 +1,33 @@
|
|
|
1
|
-
import { join, ListType, serialize, serializeArray } from './helpers'
|
|
2
|
-
import { Token, TokenAmount, USD } from './tokens'
|
|
3
|
-
import { Address, BigInt, ChainId } from './types'
|
|
4
|
-
import { Swap, Transfer, Call } from './intents'
|
|
5
|
-
import { Call as CallQuery } from './queries'
|
|
6
1
|
import { JSON } from 'json-as/assembly'
|
|
2
|
+
|
|
7
3
|
import { Context, SerializableContext } from './context'
|
|
4
|
+
import { evm } from './evm'
|
|
5
|
+
import { Consensus, ListType, MIMIC_HELPER_ADDRESS } from './helpers'
|
|
6
|
+
import { EvmCall, SvmCall, Swap, Transfer } from './intents'
|
|
7
|
+
import {
|
|
8
|
+
EvmCallQuery,
|
|
9
|
+
EvmCallQueryResponse,
|
|
10
|
+
RelevantTokensQuery,
|
|
11
|
+
RelevantTokensQueryResponse,
|
|
12
|
+
SubgraphQuery,
|
|
13
|
+
SubgraphQueryResponse,
|
|
14
|
+
SubgraphQueryResult,
|
|
15
|
+
SvmAccountsInfoQuery,
|
|
16
|
+
SvmAccountsInfoQueryResponse,
|
|
17
|
+
SvmAccountsInfoQueryResult,
|
|
18
|
+
TokenBalanceQuery,
|
|
19
|
+
TokenPriceQuery,
|
|
20
|
+
TokenPriceQueryResponse,
|
|
21
|
+
} from './queries'
|
|
22
|
+
import { BlockchainToken, Token, TokenAmount, USD } from './tokens'
|
|
23
|
+
import { Address, BigInt, Bytes, ChainId, EvmDecodeParam, EvmEncodeParam, Result } from './types'
|
|
8
24
|
|
|
9
25
|
export namespace environment {
|
|
10
|
-
@external('environment', '
|
|
11
|
-
declare function
|
|
26
|
+
@external('environment', '_evmCall')
|
|
27
|
+
declare function _evmCall(params: string): void
|
|
28
|
+
|
|
29
|
+
@external('environment', '_svmCall')
|
|
30
|
+
declare function _svmCall(params: string): void
|
|
12
31
|
|
|
13
32
|
@external('environment', '_swap')
|
|
14
33
|
declare function _swap(params: string): void
|
|
@@ -16,24 +35,38 @@ export namespace environment {
|
|
|
16
35
|
@external('environment', '_transfer')
|
|
17
36
|
declare function _transfer(params: string): void
|
|
18
37
|
|
|
19
|
-
@external('environment', '
|
|
20
|
-
declare function
|
|
38
|
+
@external('environment', '_tokenPriceQuery')
|
|
39
|
+
declare function _tokenPriceQuery(params: string): string
|
|
40
|
+
|
|
41
|
+
@external('environment', '_relevantTokensQuery')
|
|
42
|
+
declare function _relevantTokensQuery(params: string): string
|
|
21
43
|
|
|
22
|
-
@external('environment', '
|
|
23
|
-
declare function
|
|
44
|
+
@external('environment', '_evmCallQuery')
|
|
45
|
+
declare function _evmCallQuery(params: string): string
|
|
24
46
|
|
|
25
|
-
@external('environment', '
|
|
26
|
-
declare function
|
|
47
|
+
@external('environment', '_subgraphQuery')
|
|
48
|
+
declare function _subgraphQuery(params: string): string
|
|
49
|
+
|
|
50
|
+
@external('environment', '_svmAccountsInfoQuery')
|
|
51
|
+
declare function _svmAccountsInfoQuery(params: string): string
|
|
27
52
|
|
|
28
53
|
@external('environment', '_getContext')
|
|
29
54
|
declare function _getContext(): string
|
|
30
55
|
|
|
31
56
|
/**
|
|
32
|
-
* Generates a Call intent containing contract calls on the blockchain.
|
|
33
|
-
* @param call - The
|
|
57
|
+
* Generates a EVM Call intent containing contract calls on the blockchain.
|
|
58
|
+
* @param call - The EvmCall intent to generate
|
|
34
59
|
*/
|
|
35
|
-
export function
|
|
36
|
-
|
|
60
|
+
export function evmCall(call: EvmCall): void {
|
|
61
|
+
_evmCall(JSON.stringify(call))
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Generates a SVM Call intent containing contract calls on the blockchain.
|
|
66
|
+
* @param call - The SvmCall intent to generate
|
|
67
|
+
*/
|
|
68
|
+
export function svmCall(call: SvmCall): void {
|
|
69
|
+
_svmCall(JSON.stringify(call))
|
|
37
70
|
}
|
|
38
71
|
|
|
39
72
|
/**
|
|
@@ -53,74 +86,153 @@ export namespace environment {
|
|
|
53
86
|
}
|
|
54
87
|
|
|
55
88
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* @
|
|
89
|
+
* Returns an aggregated price (by consensus function) for a token in USD at a specific timestamp.
|
|
90
|
+
* By default, returns the median USD price across multiple sources.
|
|
91
|
+
*
|
|
92
|
+
* @param token - The token to get the USD price for.
|
|
93
|
+
* @param timestamp - Optional. The timestamp for price lookup (defaults to current time if not provided).
|
|
94
|
+
* @param consensusFn - Optional. A function for aggregating the price values (default is median).
|
|
95
|
+
* @returns A `Result` containing either the consensus USD price or an error string.
|
|
60
96
|
*/
|
|
61
|
-
export function
|
|
62
|
-
|
|
63
|
-
|
|
97
|
+
export function tokenPriceQuery(
|
|
98
|
+
token: Token,
|
|
99
|
+
timestamp: Date | null = null,
|
|
100
|
+
consensusFn: (values: USD[]) => USD = Consensus.medianUSD
|
|
101
|
+
): Result<USD, string> {
|
|
102
|
+
if (token.isUSD()) return Result.ok<USD, string>(USD.fromI32(1))
|
|
103
|
+
if (!(token instanceof BlockchainToken)) {
|
|
104
|
+
return Result.err<USD, string>('Price query not supported for token ' + token.toString())
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const responseStr = _tokenPriceQuery(
|
|
108
|
+
JSON.stringify(TokenPriceQuery.fromToken(changetype<BlockchainToken>(token), timestamp))
|
|
109
|
+
)
|
|
110
|
+
const pricesResult = TokenPriceQueryResponse.fromJson<TokenPriceQueryResponse>(responseStr).toResult()
|
|
111
|
+
|
|
112
|
+
if (pricesResult.isError) return Result.err<USD, string>(pricesResult.error)
|
|
113
|
+
|
|
114
|
+
const prices = pricesResult.unwrap()
|
|
115
|
+
if (prices.length === 0) return Result.err<USD, string>('Prices not found for token ' + token.toString())
|
|
116
|
+
|
|
117
|
+
return Result.ok<USD, string>(consensusFn(prices))
|
|
64
118
|
}
|
|
65
119
|
|
|
66
120
|
/**
|
|
67
|
-
*
|
|
121
|
+
* Returns the balances of an address for the specified tokens and chains.
|
|
68
122
|
* @param address - The address to query balances for
|
|
69
123
|
* @param chainIds - Array of chain ids to search
|
|
70
124
|
* @param usdMinAmount - Minimum USD value threshold for tokens (optional, defaults to zero)
|
|
71
|
-
* @param tokensList - List of tokens to include/exclude (optional, defaults to empty array)
|
|
125
|
+
* @param tokensList - List of blockchain tokens to include/exclude (optional, defaults to empty array)
|
|
72
126
|
* @param listType - Whether to include (AllowList) or exclude (DenyList) the tokens in `tokensList` (optional, defaults to DenyList)
|
|
73
|
-
* @
|
|
127
|
+
* @param consensusFn - Optional. A function for aggregating the token amounts
|
|
128
|
+
* @returns Result containing either an array of TokenAmount objects representing the relevant tokens or an error string
|
|
74
129
|
*/
|
|
75
|
-
export function
|
|
130
|
+
export function relevantTokensQuery(
|
|
76
131
|
address: Address,
|
|
77
132
|
chainIds: ChainId[],
|
|
78
133
|
usdMinAmount: USD = USD.zero(),
|
|
79
|
-
tokensList:
|
|
80
|
-
listType: ListType = ListType.DenyList
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
// this is a workaround until a decision is made regarding the timestamp
|
|
86
|
-
join([serialize(address), serializeArray(chainIds), serialize(usdMinAmount.value), serializeArray(tokensList), serialize(listType), serialize('')])
|
|
134
|
+
tokensList: BlockchainToken[] = [],
|
|
135
|
+
listType: ListType = ListType.DenyList,
|
|
136
|
+
consensusFn: (amounts: TokenBalanceQuery[][]) => TokenAmount[] = Consensus.uniqueTokenAmounts
|
|
137
|
+
): Result<TokenAmount[], string> {
|
|
138
|
+
const responseStr = _relevantTokensQuery(
|
|
139
|
+
JSON.stringify(RelevantTokensQuery.init(address, chainIds, usdMinAmount, tokensList, listType))
|
|
87
140
|
)
|
|
88
|
-
const
|
|
89
|
-
const tokenAmounts: TokenAmount[] = []
|
|
141
|
+
const responseResult = RelevantTokensQueryResponse.fromJson<RelevantTokensQueryResponse>(responseStr).toResult()
|
|
90
142
|
|
|
91
|
-
|
|
92
|
-
if (rows[i].length === 0) continue
|
|
93
|
-
tokenAmounts.push(TokenAmount.parse(rows[i]))
|
|
94
|
-
}
|
|
143
|
+
if (responseResult.isError) return Result.err<TokenAmount[], string>(responseResult.error)
|
|
95
144
|
|
|
96
|
-
return
|
|
145
|
+
return Result.ok<TokenAmount[], string>(consensusFn(responseResult.unwrap()))
|
|
97
146
|
}
|
|
98
147
|
|
|
99
148
|
/**
|
|
100
|
-
*
|
|
149
|
+
* Executes a read-only contract call on the blockchain and returns the result.
|
|
101
150
|
* @param to - The contract address to call
|
|
102
151
|
* @param chainId - The blockchain network identifier
|
|
103
152
|
* @param timestamp - The timestamp for the call context (optional)
|
|
104
153
|
* @param data - The encoded function call data
|
|
105
154
|
* @returns The raw response from the contract call
|
|
106
155
|
*/
|
|
107
|
-
export function
|
|
156
|
+
export function evmCallQuery(
|
|
108
157
|
to: Address,
|
|
109
158
|
chainId: ChainId,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
): string {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
159
|
+
data: string,
|
|
160
|
+
timestamp: Date | null = null
|
|
161
|
+
): Result<string, string> {
|
|
162
|
+
const responseStr = _evmCallQuery(JSON.stringify(EvmCallQuery.from(to, chainId, timestamp, data)))
|
|
163
|
+
return EvmCallQueryResponse.fromJson<EvmCallQueryResponse>(responseStr).toResult()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Executes a subgraph query and returns the result.
|
|
168
|
+
* @param chainId - The blockchain network identifier
|
|
169
|
+
* @param subgraphId - The ID of the subgraph to be called
|
|
170
|
+
* @param query - The string representing the subgraph query to be executed
|
|
171
|
+
* @param timestamp - The timestamp for the query context (optional)
|
|
172
|
+
* @returns The subgraph query response
|
|
173
|
+
*/
|
|
174
|
+
export function subgraphQuery(
|
|
175
|
+
chainId: ChainId,
|
|
176
|
+
subgraphId: string,
|
|
177
|
+
query: string,
|
|
178
|
+
timestamp: Date | null = null
|
|
179
|
+
): Result<SubgraphQueryResult, string> {
|
|
180
|
+
const responseStr = _subgraphQuery(JSON.stringify(SubgraphQuery.from(chainId, subgraphId, query, timestamp)))
|
|
181
|
+
return SubgraphQueryResponse.fromJson<SubgraphQueryResponse>(responseStr).toResult()
|
|
116
182
|
}
|
|
117
183
|
|
|
118
184
|
/**
|
|
119
|
-
*
|
|
120
|
-
* @
|
|
185
|
+
* Returns on-chain account info for Solana accounts.
|
|
186
|
+
* @param publicKeys - Accounts to read from chain
|
|
187
|
+
* @param timestamp - The timestamp for the call context (optional)
|
|
188
|
+
* @returns Result containing either the account info result or an error string
|
|
189
|
+
*/
|
|
190
|
+
export function svmAccountsInfoQuery(
|
|
191
|
+
publicKeys: Address[],
|
|
192
|
+
timestamp: Date | null = null
|
|
193
|
+
): Result<SvmAccountsInfoQueryResult, string> {
|
|
194
|
+
const responseStr = _svmAccountsInfoQuery(JSON.stringify(SvmAccountsInfoQuery.from(publicKeys, timestamp)))
|
|
195
|
+
return SvmAccountsInfoQueryResponse.fromJson<SvmAccountsInfoQueryResponse>(responseStr).toResult()
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Returns the current execution context containing environment information.
|
|
200
|
+
* @returns The Context object containing: user, settler, timestamp, consensusThreshold and triggerPayload
|
|
121
201
|
*/
|
|
122
202
|
export function getContext(): Context {
|
|
123
203
|
const context = JSON.parse<SerializableContext>(_getContext())
|
|
124
204
|
return Context.fromSerializable(context)
|
|
125
205
|
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Returns the native token balance of target account.
|
|
209
|
+
* @param chainId - Chain id to check balance
|
|
210
|
+
* @param target - Address to get balance from
|
|
211
|
+
* @returns The native token balance in wei
|
|
212
|
+
*/
|
|
213
|
+
export function getNativeTokenBalance(chainId: ChainId, target: Address): Result<BigInt, string> {
|
|
214
|
+
if (chainId === ChainId.SOLANA_MAINNET) return Result.err<BigInt, string>('Solana not supported')
|
|
215
|
+
const data = '0xeffd663c' + evm.encode([EvmEncodeParam.fromValue('address', target)])
|
|
216
|
+
const response = evmCallQuery(Address.fromHexString(MIMIC_HELPER_ADDRESS), chainId, data)
|
|
217
|
+
if (response.isError) return Result.err<BigInt, string>(response.error)
|
|
218
|
+
const decodedResponse = evm.decode(new EvmDecodeParam('uint256', response.unwrap()))
|
|
219
|
+
const decoded = BigInt.fromString(decodedResponse)
|
|
220
|
+
return Result.ok<BigInt, string>(decoded)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Returns the code of the target account.
|
|
225
|
+
* @param chainId - Chain id to check code
|
|
226
|
+
* @param target - Address to get code from
|
|
227
|
+
* @returns The code of the target account
|
|
228
|
+
*/
|
|
229
|
+
export function getCode(chainId: ChainId, target: Address): Result<Bytes, string> {
|
|
230
|
+
if (chainId === ChainId.SOLANA_MAINNET) return Result.err<Bytes, string>('Solana not supported')
|
|
231
|
+
const data = '0x7e105ce2' + evm.encode([EvmEncodeParam.fromValue('address', target)])
|
|
232
|
+
const response = evmCallQuery(Address.fromHexString(MIMIC_HELPER_ADDRESS), chainId, data)
|
|
233
|
+
if (response.isError) return Result.err<Bytes, string>(response.error)
|
|
234
|
+
const decodedResponse = evm.decode(new EvmDecodeParam('bytes', response.unwrap()))
|
|
235
|
+
const decoded = Bytes.fromHexString(decodedResponse)
|
|
236
|
+
return Result.ok<Bytes, string>(decoded)
|
|
237
|
+
}
|
|
126
238
|
}
|
package/src/evm.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { JSON } from 'json-as/assembly'
|
|
2
|
+
|
|
1
3
|
import { EvmDecodeParam, EvmEncodeParam } from './types'
|
|
2
|
-
import { join, serialize, serializeArray } from './helpers'
|
|
3
4
|
|
|
4
5
|
export namespace evm {
|
|
5
6
|
@external('evm', '_encode')
|
|
@@ -10,14 +11,14 @@ export namespace evm {
|
|
|
10
11
|
|
|
11
12
|
@external('evm', '_keccak')
|
|
12
13
|
declare function _keccak(params: string): string
|
|
13
|
-
|
|
14
|
+
|
|
14
15
|
/**
|
|
15
16
|
* Encodes parameters for EVM smart contract function calls using ABI encoding.
|
|
16
17
|
* @param callParameters - Array of parameters to encode for the contract call
|
|
17
18
|
* @returns The ABI-encoded data as a hex string
|
|
18
19
|
*/
|
|
19
20
|
export function encode(callParameters: EvmEncodeParam[]): string {
|
|
20
|
-
return _encode(
|
|
21
|
+
return _encode(JSON.stringify(callParameters))
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -26,7 +27,7 @@ export namespace evm {
|
|
|
26
27
|
* @returns The decoded data as a formatted string
|
|
27
28
|
*/
|
|
28
29
|
export function decode(encodedData: EvmDecodeParam): string {
|
|
29
|
-
return _decode(
|
|
30
|
+
return _decode(JSON.stringify(encodedData))
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
/**
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Address, ByteArray, Bytes } from '../types'
|
|
2
|
+
import { Option } from '../types/Option'
|
|
3
|
+
|
|
4
|
+
import { bytesToString } from './strings'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Unsafe class to deserialize bytes into Rust-like types
|
|
8
|
+
* Should be used with caution! This will only throw if there are insufficient bytes for the type
|
|
9
|
+
*/
|
|
10
|
+
export class BorshDeserializer {
|
|
11
|
+
private _bytes: Uint8Array
|
|
12
|
+
private _offset: u32 = 0
|
|
13
|
+
|
|
14
|
+
constructor(bytes: Uint8Array) {
|
|
15
|
+
this._bytes = bytes
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static fromHex(hex: string): BorshDeserializer {
|
|
19
|
+
return new BorshDeserializer(Bytes.fromHexString(hex))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static fromBytes(bytes: Uint8Array): BorshDeserializer {
|
|
23
|
+
return new BorshDeserializer(bytes)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
tryBool(): bool {
|
|
27
|
+
if (this._offset >= this.getBytesLength()) throw new Error('Insufficient bytes for bool')
|
|
28
|
+
const value = this._bytes.at(this._offset)
|
|
29
|
+
this._offset += 1
|
|
30
|
+
return value === 1
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
tryU8(): u8 {
|
|
34
|
+
if (this._offset >= this.getBytesLength()) throw new Error('Insufficient bytes for u8')
|
|
35
|
+
const value = this._bytes.at(this._offset)
|
|
36
|
+
this._offset += 1
|
|
37
|
+
return value
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
tryU16(): u16 {
|
|
41
|
+
if (this._offset + 1 >= this.getBytesLength()) throw new Error('Insufficient bytes for u16')
|
|
42
|
+
const subarray = changetype<ByteArray>(this._bytes.subarray(this._offset, this._offset + 2))
|
|
43
|
+
this._offset += 2
|
|
44
|
+
return subarray.toU16()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
tryU32(): u32 {
|
|
48
|
+
if (this._offset + 3 >= this.getBytesLength()) throw new Error('Insufficient bytes for u32')
|
|
49
|
+
const subarray = changetype<ByteArray>(this._bytes.subarray(this._offset, this._offset + 4))
|
|
50
|
+
this._offset += 4
|
|
51
|
+
return subarray.toU32()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
tryU64(): u64 {
|
|
55
|
+
if (this._offset + 7 >= this.getBytesLength()) throw new Error('Insufficient bytes for u64')
|
|
56
|
+
const subarray = changetype<ByteArray>(this._bytes.subarray(this._offset, this._offset + 8))
|
|
57
|
+
this._offset += 8
|
|
58
|
+
return subarray.toU64()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
tryPubkey(): Address {
|
|
62
|
+
if (this._offset + 31 >= this.getBytesLength()) throw new Error('Insufficient bytes for pubkey')
|
|
63
|
+
const pubkey = Address.fromBytes(Bytes.fromUint8Array(this._bytes.subarray(this._offset, this._offset + 32)))
|
|
64
|
+
this._offset += 32
|
|
65
|
+
return pubkey
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
tryString(): string {
|
|
69
|
+
const length = this.tryU32()
|
|
70
|
+
if (this._offset + length - 1 >= this.getBytesLength())
|
|
71
|
+
throw new Error(`Insufficient bytes for string of size ${length}`)
|
|
72
|
+
const str = bytesToString(this._bytes.subarray(this._offset, this._offset + length), true)
|
|
73
|
+
this._offset += length
|
|
74
|
+
return str
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
tryOptionBool(): Option<bool> {
|
|
78
|
+
const tag = this.tryU32()
|
|
79
|
+
if (tag === 0) return Option.none<bool>()
|
|
80
|
+
return Option.some<bool>(this.tryBool())
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
tryOptionU8(): Option<u8> {
|
|
84
|
+
const tag = this.tryU32()
|
|
85
|
+
if (tag === 0) return Option.none<u8>()
|
|
86
|
+
return Option.some<u8>(this.tryU8())
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
tryOptionU16(): Option<u16> {
|
|
90
|
+
const tag = this.tryU32()
|
|
91
|
+
if (tag === 0) return Option.none<u16>()
|
|
92
|
+
return Option.some<u16>(this.tryU16())
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
tryOptionU32(): Option<u32> {
|
|
96
|
+
const tag = this.tryU32()
|
|
97
|
+
if (tag === 0) return Option.none<u32>()
|
|
98
|
+
return Option.some<u32>(this.tryU32())
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
tryOptionU64(): Option<u64> {
|
|
102
|
+
const tag = this.tryU32()
|
|
103
|
+
if (tag === 0) return Option.none<u64>()
|
|
104
|
+
return Option.some<u64>(this.tryU64())
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
tryOptionPubkey(): Option<Address> {
|
|
108
|
+
const tag = this.tryU32()
|
|
109
|
+
if (tag === 0) return Option.none<Address>(Address.fromBytes(new Bytes(32)))
|
|
110
|
+
return Option.some<Address>(this.tryPubkey())
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private getBytesLength(): u32 {
|
|
114
|
+
return this._bytes.length as u32
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
getOffset(): u32 {
|
|
118
|
+
return this._offset
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
setOffset(offset: u32): void {
|
|
122
|
+
if (offset > this.getBytesLength()) throw new Error('Offset overruns buffer length')
|
|
123
|
+
this._offset = offset
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
isEmpty(): bool {
|
|
127
|
+
return this._offset >= this.getBytesLength()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
getLength(): u32 {
|
|
131
|
+
return this._bytes.length
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { TokenBalanceQuery } from '../queries/RelevantTokensQuery'
|
|
2
|
+
import { TokenAmount, USD } from '../tokens'
|
|
3
|
+
import { BigInt } from '../types'
|
|
4
|
+
|
|
5
|
+
import { median } from './math'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Computes the median value from an array of USD values.
|
|
9
|
+
* @param values - Array of USD values to compute the median from
|
|
10
|
+
* @returns The median USD value
|
|
11
|
+
* @throws Error if the array is empty
|
|
12
|
+
*/
|
|
13
|
+
export function medianUSD(values: USD[]): USD {
|
|
14
|
+
return USD.fromBigInt(median(values.map<BigInt>((value: USD) => value.value)))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Deduplicates token balances by token address.
|
|
19
|
+
* Converts TokenBalanceQuery[][] to TokenAmount[] keeping only the first occurrence of each token address.
|
|
20
|
+
* @param balances - Array of arrays of TokenBalanceQuery to deduplicate
|
|
21
|
+
* @returns Array of unique TokenAmount objects
|
|
22
|
+
*/
|
|
23
|
+
export function uniqueTokenAmounts(balances: TokenBalanceQuery[][]): TokenAmount[] {
|
|
24
|
+
const resultMap: Map<string, TokenAmount> = new Map()
|
|
25
|
+
for (let i = 0; i < balances.length; i++) {
|
|
26
|
+
for (let j = 0; j < balances[i].length; j++) {
|
|
27
|
+
const tokenAmount = balances[i][j].toTokenAmount()
|
|
28
|
+
const mapKey = tokenAmount.token.address.toString()
|
|
29
|
+
|
|
30
|
+
if (resultMap.has(mapKey)) continue
|
|
31
|
+
resultMap.set(mapKey, tokenAmount)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return resultMap.values()
|
|
35
|
+
}
|
package/src/helpers/constants.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000'
|
|
2
|
-
export const
|
|
2
|
+
export const EVM_NATIVE_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
|
|
3
|
+
export const SVM_NATIVE_ADDRESS = 'Nativeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
|
|
4
|
+
export const USD_ADDRESS = '0x0000000000000000000000000000000000000348'
|
|
5
|
+
|
|
6
|
+
export const MAX_UINT256_HEX = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
|
|
3
7
|
|
|
4
8
|
export const STANDARD_DECIMALS: u8 = 18
|
|
5
9
|
|
|
@@ -7,3 +11,5 @@ export enum ListType {
|
|
|
7
11
|
AllowList = 0,
|
|
8
12
|
DenyList = 1,
|
|
9
13
|
}
|
|
14
|
+
|
|
15
|
+
export const MIMIC_HELPER_ADDRESS = '0x5cf82cbed1110fc2f75b3413d53abac492931804'
|
package/src/helpers/index.ts
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BigInt } from '../types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Computes the median value from an array of BigInt values.
|
|
5
|
+
* @param values - Array of BigInt values to compute the median from
|
|
6
|
+
* @returns The median BigInt value
|
|
7
|
+
* @throws Error if the array is empty
|
|
8
|
+
*/
|
|
9
|
+
export function median(values: BigInt[]): BigInt {
|
|
10
|
+
if (values.length === 0) throw new Error('Cannot compute median of empty array')
|
|
11
|
+
|
|
12
|
+
const sorted = values.sort((a: BigInt, b: BigInt) => BigInt.compare(a, b))
|
|
13
|
+
const len = sorted.length
|
|
14
|
+
|
|
15
|
+
if (len % 2 === 1) return sorted[len / 2]
|
|
16
|
+
|
|
17
|
+
const left = sorted[len / 2 - 1]
|
|
18
|
+
const right = sorted[len / 2]
|
|
19
|
+
return left.plus(right).div(BigInt.fromI32(2))
|
|
20
|
+
}
|
package/src/helpers/serialize.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { Bytes } from '../types'
|
|
2
2
|
|
|
3
3
|
export interface Stringable {
|
|
4
4
|
toString(): string
|
|
@@ -8,133 +8,13 @@ export interface Serializable {
|
|
|
8
8
|
serialize(): string
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
export interface Byteable {
|
|
12
|
+
toBytes(): Bytes
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
export function serialize<T extends Stringable>(elem: T): string {
|
|
12
16
|
// eslint-disable-next-line
|
|
13
17
|
// @ts-ignore
|
|
14
18
|
if (elem instanceof Serializable) return elem.serialize()
|
|
15
19
|
return elem.toString()
|
|
16
20
|
}
|
|
17
|
-
|
|
18
|
-
export function serializeArray<T extends Stringable>(array: T[]): string {
|
|
19
|
-
const serializedElems: (string | null)[] = []
|
|
20
|
-
for (let i = 0; i < array.length; i++) {
|
|
21
|
-
serializedElems.push(serialize(array[i]))
|
|
22
|
-
}
|
|
23
|
-
return 'Array(' + join(serializedElems) + ')'
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function join(lst: (string | null)[]): string {
|
|
27
|
-
return lst.join(SEPARATOR)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Parses a CSV string into an array of tokens, handling nested structures,
|
|
32
|
-
* null values, and stripping one layer of outer parentheses if they wrap the entire string.
|
|
33
|
-
*
|
|
34
|
-
* @param csvString - String containing comma-separated values, nested structures (e.g., "(1,2)"), and nulls
|
|
35
|
-
* @returns Array of strings (trimmed) and nulls, preserving internal nesting
|
|
36
|
-
* @throws {Error} If parentheses are unbalanced
|
|
37
|
-
*/
|
|
38
|
-
export function parseCSV(csvString: string): (string | null)[] {
|
|
39
|
-
const tokens: (string | null)[] = []
|
|
40
|
-
const currentTokenChars: string[] = []
|
|
41
|
-
const len = csvString.length
|
|
42
|
-
let depth: i32 = 0
|
|
43
|
-
let isEmpty = true
|
|
44
|
-
|
|
45
|
-
let shouldStripOuter = false
|
|
46
|
-
if (len > 1 && csvString.charAt(0) == '(' && csvString.charAt(len - 1) == ')') {
|
|
47
|
-
let balance = 0
|
|
48
|
-
let firstParenMatchesLast = true
|
|
49
|
-
for (let k = 0; k < len; k++) {
|
|
50
|
-
if (csvString.charAt(k) == '(') {
|
|
51
|
-
balance++
|
|
52
|
-
} else if (csvString.charAt(k) == ')') {
|
|
53
|
-
balance--
|
|
54
|
-
}
|
|
55
|
-
if (balance == 0 && k < len - 1) {
|
|
56
|
-
firstParenMatchesLast = false
|
|
57
|
-
break
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (firstParenMatchesLast && balance == 0) {
|
|
61
|
-
shouldStripOuter = true
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
for (let i = 0; i < len; i++) {
|
|
66
|
-
const char = csvString.charAt(i)
|
|
67
|
-
const charCode = char.charCodeAt(0)
|
|
68
|
-
|
|
69
|
-
switch (charCode) {
|
|
70
|
-
case '('.charCodeAt(0):
|
|
71
|
-
if (shouldStripOuter && i == 0) {
|
|
72
|
-
depth++
|
|
73
|
-
break
|
|
74
|
-
}
|
|
75
|
-
depth++
|
|
76
|
-
currentTokenChars.push(char)
|
|
77
|
-
isEmpty = false
|
|
78
|
-
break
|
|
79
|
-
|
|
80
|
-
case ')'.charCodeAt(0):
|
|
81
|
-
if (shouldStripOuter && i == len - 1) {
|
|
82
|
-
depth--
|
|
83
|
-
break
|
|
84
|
-
}
|
|
85
|
-
depth--
|
|
86
|
-
if (depth < 0) throw new Error(`Unbalanced parentheses at position ${i}`)
|
|
87
|
-
currentTokenChars.push(char)
|
|
88
|
-
isEmpty = false
|
|
89
|
-
break
|
|
90
|
-
|
|
91
|
-
case SEPARATOR.charCodeAt(0):
|
|
92
|
-
const isTopLevelComma = (shouldStripOuter && depth == 1) || (!shouldStripOuter && depth == 0)
|
|
93
|
-
if (isTopLevelComma) {
|
|
94
|
-
if (isEmpty) {
|
|
95
|
-
tokens.push(null)
|
|
96
|
-
} else {
|
|
97
|
-
tokens.push(currentTokenChars.join('').trim())
|
|
98
|
-
}
|
|
99
|
-
currentTokenChars.length = 0
|
|
100
|
-
isEmpty = true
|
|
101
|
-
} else {
|
|
102
|
-
currentTokenChars.push(char)
|
|
103
|
-
isEmpty = false
|
|
104
|
-
}
|
|
105
|
-
break
|
|
106
|
-
|
|
107
|
-
default:
|
|
108
|
-
currentTokenChars.push(char)
|
|
109
|
-
isEmpty = false
|
|
110
|
-
break
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (depth != 0) throw new Error('Unbalanced parentheses at the end of the string')
|
|
115
|
-
|
|
116
|
-
if (len > 0) {
|
|
117
|
-
if (isEmpty) {
|
|
118
|
-
tokens.push(null)
|
|
119
|
-
} else {
|
|
120
|
-
tokens.push(currentTokenChars.join('').trim())
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return tokens
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Parses a CSV string into an array of strings, throwing an error if any null values are present.
|
|
129
|
-
*
|
|
130
|
-
* @param csvString - String containing comma-separated values
|
|
131
|
-
* @returns Array of strings
|
|
132
|
-
* @throws {Error} If null values are present
|
|
133
|
-
*/
|
|
134
|
-
|
|
135
|
-
export function parseCSVNotNullable(csvString: string): string[] {
|
|
136
|
-
const tokens = parseCSV(csvString)
|
|
137
|
-
if (tokens.some((token) => token === null)) throw new Error('Null value found in CSV string')
|
|
138
|
-
|
|
139
|
-
return changetype<string[]>(tokens)
|
|
140
|
-
}
|