@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.
Files changed (77) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/README.md +7 -7
  3. package/constants.d.ts +1 -0
  4. package/constants.js +1 -0
  5. package/index.ts +3 -0
  6. package/package.json +18 -4
  7. package/src/chains/Arbitrum.ts +14 -0
  8. package/src/chains/Avalanche.ts +15 -0
  9. package/src/chains/BNB.ts +15 -0
  10. package/src/chains/Base.ts +14 -0
  11. package/src/chains/BaseSepolia.ts +7 -0
  12. package/src/chains/Ethereum.ts +7 -7
  13. package/src/chains/Gnosis.ts +14 -0
  14. package/src/chains/Optimism.ts +7 -7
  15. package/src/chains/Polygon.ts +10 -7
  16. package/src/chains/Sonic.ts +13 -0
  17. package/src/chains/index.ts +7 -0
  18. package/src/context/Context.ts +102 -7
  19. package/src/environment.ts +165 -53
  20. package/src/evm.ts +5 -4
  21. package/src/helpers/BorshDeserializer.ts +133 -0
  22. package/src/helpers/consensus.ts +35 -0
  23. package/src/helpers/constants.ts +7 -1
  24. package/src/helpers/index.ts +5 -0
  25. package/src/helpers/math.ts +20 -0
  26. package/src/helpers/serialize.ts +5 -125
  27. package/src/helpers/strings.ts +82 -5
  28. package/src/intents/Call/EvmCall.ts +283 -0
  29. package/src/intents/Call/SvmCall.ts +278 -0
  30. package/src/intents/Call/index.ts +2 -0
  31. package/src/intents/Intent.ts +178 -5
  32. package/src/intents/Swap.ts +136 -44
  33. package/src/intents/Transfer.ts +103 -58
  34. package/src/log.ts +83 -0
  35. package/src/queries/EvmCallQuery.ts +43 -0
  36. package/src/queries/QueryResponse.ts +26 -0
  37. package/src/queries/RelevantTokensQuery.ts +82 -0
  38. package/src/queries/SubgraphQuery.ts +50 -0
  39. package/src/queries/SvmAccountsInfoQuery.ts +65 -0
  40. package/src/queries/TokenPriceQuery.ts +47 -0
  41. package/src/queries/index.ts +6 -1
  42. package/src/storage/index.ts +1 -0
  43. package/src/storage/storage.ts +46 -0
  44. package/src/svm.ts +27 -0
  45. package/src/tokens/BlockchainToken.ts +108 -0
  46. package/src/tokens/DenominationToken.ts +70 -0
  47. package/src/tokens/ERC20Token.ts +192 -0
  48. package/src/tokens/SPLToken.ts +162 -0
  49. package/src/tokens/Token.ts +55 -155
  50. package/src/tokens/TokenAmount.ts +72 -30
  51. package/src/tokens/TokenProvider.ts +54 -0
  52. package/src/tokens/Tokens.ts +186 -0
  53. package/src/tokens/USD.ts +9 -6
  54. package/src/tokens/index.ts +6 -0
  55. package/src/types/Address.ts +86 -14
  56. package/src/types/BigInt.ts +14 -22
  57. package/src/types/ByteArray.ts +41 -3
  58. package/src/types/Bytes.ts +7 -0
  59. package/src/types/ChainId.ts +9 -1
  60. package/src/types/Option.ts +35 -0
  61. package/src/types/Result.ts +68 -0
  62. package/src/types/TriggerType.ts +4 -0
  63. package/src/types/evm/EvmDecodeParam.ts +7 -0
  64. package/src/types/evm/EvmEncodeParam.ts +31 -0
  65. package/src/types/evm/index.ts +2 -0
  66. package/src/types/index.ts +8 -2
  67. package/src/types/svm/SvmAccountInfo.ts +32 -0
  68. package/src/types/svm/SvmAccountMeta.ts +28 -0
  69. package/src/types/svm/SvmFindProgramAddress.ts +32 -0
  70. package/src/types/svm/SvmMint.ts +44 -0
  71. package/src/types/svm/SvmPdaSeed.ts +19 -0
  72. package/src/types/svm/SvmTokenMetadataData.ts +29 -0
  73. package/src/types/svm/index.ts +5 -0
  74. package/src/intents/Call.ts +0 -238
  75. package/src/queries/Call.ts +0 -16
  76. package/src/types/EvmDecodeParam.ts +0 -30
  77. package/src/types/EvmEncodeParam.ts +0 -54
@@ -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', '_call')
11
- declare function _call(params: string): void
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', '_getPrice')
20
- declare function _getPrice(params: string): string
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', '_getRelevantTokens')
23
- declare function _getRelevantTokens(params: string): string
44
+ @external('environment', '_evmCallQuery')
45
+ declare function _evmCallQuery(params: string): string
24
46
 
25
- @external('environment', '_contractCall')
26
- declare function _contractCall(params: string): string
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 Call intent to generate
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 call(call: Call): void {
36
- _call(JSON.stringify(call))
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
- * Tells the price of a token in USD at a specific timestamp.
57
- * @param token - The token to get the price of
58
- * @param timestamp - The timestamp for price lookup (optional, defaults to current time)
59
- * @returns The token price in USD
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 getPrice(token: Token, timestamp: Date | null = null): USD {
62
- const price = _getPrice(join([serialize(token.address), serialize(token.chainId), serialize(timestamp ? timestamp.getTime().toString() : '')]))
63
- return USD.fromBigInt(BigInt.fromString(price))
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
- * Tells the balances of an address for the specified tokens and chains.
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
- * @returns Array of TokenAmount objects representing the relevant tokens
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 getRelevantTokens(
130
+ export function relevantTokensQuery(
76
131
  address: Address,
77
132
  chainIds: ChainId[],
78
133
  usdMinAmount: USD = USD.zero(),
79
- tokensList: Token[] = [],
80
- listType: ListType = ListType.DenyList
81
- ): TokenAmount[] {
82
- const response = _getRelevantTokens(
83
- // NOTE: The runner expects an optional timestamp that the user will not be able to input
84
- // that's why serialize('') is used
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 rows = response.split('\n')
89
- const tokenAmounts: TokenAmount[] = []
141
+ const responseResult = RelevantTokensQueryResponse.fromJson<RelevantTokensQueryResponse>(responseStr).toResult()
90
142
 
91
- for (let i = 0; i < rows.length; i++) {
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 tokenAmounts
145
+ return Result.ok<TokenAmount[], string>(consensusFn(responseResult.unwrap()))
97
146
  }
98
147
 
99
148
  /**
100
- * Generates a contract call of a read function on the blockchain and returns the result.
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 contractCall(
156
+ export function evmCallQuery(
108
157
  to: Address,
109
158
  chainId: ChainId,
110
- timestamp: Date | null,
111
- data: string
112
- ): string {
113
- return _contractCall(
114
- JSON.stringify(new CallQuery(to, chainId, timestamp, data))
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
- * Tells the current execution context containing environment information.
120
- * @returns The Context object containing: user, settler, timestamp, and config ID
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(join([serializeArray(callParameters)]))
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(serialize(encodedData))
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
+ }
@@ -1,5 +1,9 @@
1
1
  export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000'
2
- export const NATIVE_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'
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'
@@ -1,3 +1,8 @@
1
+ import * as Consensus from './consensus'
2
+ import * as Math from './math'
3
+
4
+ export * from './BorshDeserializer'
1
5
  export * from './constants'
2
6
  export * from './serialize'
3
7
  export * from './strings'
8
+ export { Consensus, Math }
@@ -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
+ }
@@ -1,4 +1,4 @@
1
- const SEPARATOR = ','
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
- }