From 859eb1d13b8c38cee64389b3dec2d916de8c9650 Mon Sep 17 00:00:00 2001 From: PedroAraoz Date: Fri, 23 Jan 2026 16:53:15 -0300 Subject: [PATCH 1/4] lib: get native balance --- packages/lib-ts/src/environment.ts | 47 +++++++++++++++++++++-------- packages/lib-ts/src/evm.ts | 5 +-- packages/lib-ts/src/log.ts | 4 +-- packages/lib-ts/src/mimichelpers.ts | 18 +++++++++++ 4 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 packages/lib-ts/src/mimichelpers.ts diff --git a/packages/lib-ts/src/environment.ts b/packages/lib-ts/src/environment.ts index 09d9e49f..3af09f0f 100644 --- a/packages/lib-ts/src/environment.ts +++ b/packages/lib-ts/src/environment.ts @@ -3,6 +3,7 @@ import { JSON } from 'json-as/assembly' import { Context, SerializableContext } from './context' import { Consensus, ListType } from './helpers' import { EvmCall, SvmCall, Swap, Transfer } from './intents' +import { mimicHelpers } from './mimichelpers' import { EvmCallQuery, EvmCallQueryResponse, @@ -19,7 +20,7 @@ import { TokenPriceQueryResponse, } from './queries' import { BlockchainToken, Token, TokenAmount, USD } from './tokens' -import { Address, ChainId, Result } from './types' +import { Address, BigInt, ChainId, Result } from './types' export namespace environment { @external('environment', '_evmCall') @@ -93,15 +94,23 @@ export namespace environment { * @param consensusFn - Optional. A function for aggregating the price values (default is median). * @returns A `Result` containing either the consensus USD price or an error string. */ - export function tokenPriceQuery(token: Token, timestamp: Date | null = null, consensusFn: (values: USD[]) => USD = Consensus.medianUSD): Result { + export function tokenPriceQuery( + token: Token, + timestamp: Date | null = null, + consensusFn: (values: USD[]) => USD = Consensus.medianUSD + ): Result { if (token.isUSD()) return Result.ok(USD.fromI32(1)) - if (!(token instanceof BlockchainToken)) return Result.err('Price query not supported for token ' + token.toString()) - - const responseStr = _tokenPriceQuery(JSON.stringify(TokenPriceQuery.fromToken(changetype(token), timestamp))) + if (!(token instanceof BlockchainToken)) { + return Result.err('Price query not supported for token ' + token.toString()) + } + + const responseStr = _tokenPriceQuery( + JSON.stringify(TokenPriceQuery.fromToken(changetype(token), timestamp)) + ) const pricesResult = TokenPriceQueryResponse.fromJson(responseStr).toResult() - + if (pricesResult.isError) return Result.err(pricesResult.error) - + const prices = pricesResult.unwrap() if (prices.length === 0) return Result.err('Prices not found for token ' + token.toString()) @@ -126,9 +135,11 @@ export namespace environment { listType: ListType = ListType.DenyList, consensusFn: (amounts: TokenBalanceQuery[][]) => TokenAmount[] = Consensus.uniqueTokenAmounts ): Result { - const responseStr = _relevantTokensQuery(JSON.stringify(RelevantTokensQuery.init(address, chainIds, usdMinAmount, tokensList, listType))) + const responseStr = _relevantTokensQuery( + JSON.stringify(RelevantTokensQuery.init(address, chainIds, usdMinAmount, tokensList, listType)) + ) const responseResult = RelevantTokensQueryResponse.fromJson(responseStr).toResult() - + if (responseResult.isError) return Result.err(responseResult.error) return Result.ok(consensusFn(responseResult.unwrap())) @@ -146,7 +157,7 @@ export namespace environment { to: Address, chainId: ChainId, data: string, - timestamp: Date | null = null, + timestamp: Date | null = null ): Result { const responseStr = _evmCallQuery(JSON.stringify(EvmCallQuery.from(to, chainId, timestamp, data))) return EvmCallQueryResponse.fromJson(responseStr).toResult() @@ -164,12 +175,12 @@ export namespace environment { chainId: ChainId, subgraphId: string, query: string, - timestamp: Date | null = null, + timestamp: Date | null = null ): Result { const responseStr = _subgraphQuery(JSON.stringify(SubgraphQuery.from(chainId, subgraphId, query, timestamp))) return SubgraphQueryResponse.fromJson(responseStr).toResult() } - + /** * Returns on-chain account info for Solana accounts. * @param publicKeys - Accounts to read from chain @@ -178,7 +189,7 @@ export namespace environment { */ export function svmAccountsInfoQuery( publicKeys: Address[], - timestamp: Date | null = null, + timestamp: Date | null = null ): Result { const responseStr = _svmAccountsInfoQuery(JSON.stringify(SvmAccountsInfoQuery.from(publicKeys, timestamp))) return SvmAccountsInfoQueryResponse.fromJson(responseStr).toResult() @@ -192,4 +203,14 @@ export namespace environment { const context = JSON.parse(_getContext()) return Context.fromSerializable(context) } + + /** + * Returns the native token balance of target account. + * @param chainId - Chain id to check balance + * @param target - Address to get balance from + * @returns The native token balance in wei + */ + export function getNativeTokenBalance(chainId: ChainId, target: Address): Result { + return mimicHelpers.getNativeTokenBalance(chainId, target) + } } diff --git a/packages/lib-ts/src/evm.ts b/packages/lib-ts/src/evm.ts index 88ff6595..6dcc3ba8 100644 --- a/packages/lib-ts/src/evm.ts +++ b/packages/lib-ts/src/evm.ts @@ -1,6 +1,7 @@ -import { EvmDecodeParam, EvmEncodeParam } from './types' import { JSON } from 'json-as/assembly' +import { EvmDecodeParam, EvmEncodeParam } from './types' + export namespace evm { @external('evm', '_encode') declare function _encode(params: string): string @@ -10,7 +11,7 @@ export namespace evm { @external('evm', '_keccak') declare function _keccak(params: string): string - + /** * Encodes parameters for EVM smart contract function calls using ABI encoding. * @param callParameters - Array of parameters to encode for the contract call diff --git a/packages/lib-ts/src/log.ts b/packages/lib-ts/src/log.ts index e080b1c3..33172d0c 100644 --- a/packages/lib-ts/src/log.ts +++ b/packages/lib-ts/src/log.ts @@ -4,7 +4,7 @@ // Copyright (c) 2018 Graph Protocol, Inc. and contributors. // Modified by Mimic Protocol, 2025. -import { Stringable } from "./helpers" +import { Stringable } from './helpers' export namespace log { @external('log', '_log') @@ -69,7 +69,7 @@ export namespace log { function format(fmt: string, args: Array): string { let out = '' let argIndex = 0 - const argsStr = args.map(a => a.toString()) + const argsStr = args.map((a) => a.toString()) for (let i: i32 = 0, len: i32 = fmt.length; i < len; i++) { if (i < len - 1 && fmt.charCodeAt(i) == 0x7b /* '{' */ && fmt.charCodeAt(i + 1) == 0x7d /* '}' */) { if (argIndex >= argsStr.length) throw new Error('Too few arguments for format string: ' + fmt) diff --git a/packages/lib-ts/src/mimichelpers.ts b/packages/lib-ts/src/mimichelpers.ts new file mode 100644 index 00000000..8637d6ac --- /dev/null +++ b/packages/lib-ts/src/mimichelpers.ts @@ -0,0 +1,18 @@ +import { environment } from './environment' +import { evm } from './evm' +import { Address, BigInt, ChainId, EvmDecodeParam, EvmEncodeParam, Result } from './types' + +// todo replace with correct address once it is deployed +const MIMIC_HELPER_ADDRESS = Address.fromHexString('0x') + +export namespace mimicHelpers { + export function getNativeTokenBalance(chainId: ChainId, target: Address): Result { + if (chainId === ChainId.SOLANA_MAINNET) return Result.err('Solana not supported') + const data = '0xeffd663c' + evm.encode([EvmEncodeParam.fromValue('address', target)]) + const response = environment.evmCallQuery(MIMIC_HELPER_ADDRESS, chainId, data) + if (response.isError) return Result.err(response.error) + const decodedResponse = evm.decode(new EvmDecodeParam('uint256', response.unwrap())) + const decoded = BigInt.fromString(decodedResponse) + return Result.ok(decoded) + } +} From 25cdaaabf3b5839f51d7f7277402d7f4bb827646 Mon Sep 17 00:00:00 2001 From: PedroAraoz Date: Mon, 26 Jan 2026 12:07:40 -0300 Subject: [PATCH 2/4] lib: move getNativeTokenBalance impl to environment --- packages/lib-ts/src/environment.ts | 14 ++++++++++---- packages/lib-ts/src/helpers/constants.ts | 3 +++ packages/lib-ts/src/mimichelpers.ts | 18 ------------------ 3 files changed, 13 insertions(+), 22 deletions(-) delete mode 100644 packages/lib-ts/src/mimichelpers.ts diff --git a/packages/lib-ts/src/environment.ts b/packages/lib-ts/src/environment.ts index 3af09f0f..258b89f1 100644 --- a/packages/lib-ts/src/environment.ts +++ b/packages/lib-ts/src/environment.ts @@ -1,9 +1,9 @@ import { JSON } from 'json-as/assembly' import { Context, SerializableContext } from './context' -import { Consensus, ListType } from './helpers' +import { evm } from './evm' +import { Consensus, ListType, MIMIC_HELPER_ADDRESS } from './helpers' import { EvmCall, SvmCall, Swap, Transfer } from './intents' -import { mimicHelpers } from './mimichelpers' import { EvmCallQuery, EvmCallQueryResponse, @@ -20,7 +20,7 @@ import { TokenPriceQueryResponse, } from './queries' import { BlockchainToken, Token, TokenAmount, USD } from './tokens' -import { Address, BigInt, ChainId, Result } from './types' +import { Address, BigInt, ChainId, EvmDecodeParam, EvmEncodeParam, Result } from './types' export namespace environment { @external('environment', '_evmCall') @@ -211,6 +211,12 @@ export namespace environment { * @returns The native token balance in wei */ export function getNativeTokenBalance(chainId: ChainId, target: Address): Result { - return mimicHelpers.getNativeTokenBalance(chainId, target) + if (chainId === ChainId.SOLANA_MAINNET) return Result.err('Solana not supported') + const data = '0xeffd663c' + evm.encode([EvmEncodeParam.fromValue('address', target)]) + const response = evmCallQuery(Address.fromHexString(MIMIC_HELPER_ADDRESS), chainId, data) + if (response.isError) return Result.err(response.error) + const decodedResponse = evm.decode(new EvmDecodeParam('uint256', response.unwrap())) + const decoded = BigInt.fromString(decodedResponse) + return Result.ok(decoded) } } diff --git a/packages/lib-ts/src/helpers/constants.ts b/packages/lib-ts/src/helpers/constants.ts index cbb2c369..33b86a73 100644 --- a/packages/lib-ts/src/helpers/constants.ts +++ b/packages/lib-ts/src/helpers/constants.ts @@ -11,3 +11,6 @@ export enum ListType { AllowList = 0, DenyList = 1, } + +// todo replace with correct address once it is deployed +export const MIMIC_HELPER_ADDRESS = '0x' diff --git a/packages/lib-ts/src/mimichelpers.ts b/packages/lib-ts/src/mimichelpers.ts deleted file mode 100644 index 8637d6ac..00000000 --- a/packages/lib-ts/src/mimichelpers.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { environment } from './environment' -import { evm } from './evm' -import { Address, BigInt, ChainId, EvmDecodeParam, EvmEncodeParam, Result } from './types' - -// todo replace with correct address once it is deployed -const MIMIC_HELPER_ADDRESS = Address.fromHexString('0x') - -export namespace mimicHelpers { - export function getNativeTokenBalance(chainId: ChainId, target: Address): Result { - if (chainId === ChainId.SOLANA_MAINNET) return Result.err('Solana not supported') - const data = '0xeffd663c' + evm.encode([EvmEncodeParam.fromValue('address', target)]) - const response = environment.evmCallQuery(MIMIC_HELPER_ADDRESS, chainId, data) - if (response.isError) return Result.err(response.error) - const decodedResponse = evm.decode(new EvmDecodeParam('uint256', response.unwrap())) - const decoded = BigInt.fromString(decodedResponse) - return Result.ok(decoded) - } -} From db921f55c05911f003340b916475a1f3ea43c145 Mon Sep 17 00:00:00 2001 From: PedroAraoz Date: Mon, 26 Jan 2026 15:53:35 -0300 Subject: [PATCH 3/4] lib: add tests for getNativeTokenBalance --- packages/lib-ts/as-pect.config.js | 14 +++++++ packages/lib-ts/src/helpers/constants.ts | 3 +- packages/lib-ts/tests/environment.spec.ts | 46 +++++++++++++++++++++++ packages/lib-ts/tests/helpers.ts | 2 + 4 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 packages/lib-ts/tests/environment.spec.ts diff --git a/packages/lib-ts/as-pect.config.js b/packages/lib-ts/as-pect.config.js index bc186b7f..d41eb67e 100644 --- a/packages/lib-ts/as-pect.config.js +++ b/packages/lib-ts/as-pect.config.js @@ -37,6 +37,13 @@ export default { }, }, evm: { + _encode: (paramsPtr) => { + const paramsStr = exports.__getString(paramsPtr) + const params = JSON.parse(paramsStr)[0] + const key = `_evmDecode:${params.abiType}:${params.value}` + if (store.has(key)) return exports.__newString(store.get(key)) + throw new Error(`Encode value not found for key: ${key}`) + }, _decode: (paramsPtr) => { const paramsStr = exports.__getString(paramsPtr) const params = JSON.parse(paramsStr) @@ -117,6 +124,13 @@ export default { const key = `_evmCallQuery:${to}:${chainId}:${data}` store.set(key, result) }, + setEvmEncode: (abiTypePtr, dataPtr, encodedPtr) => { + const abiType = exports.__getString(abiTypePtr) + const data = exports.__getString(dataPtr) + const key = `_evmDecode:${abiType}:${data}` + const encoded = exports.__getString(encodedPtr) + store.set(key, encoded) + }, setEvmDecode: (abiTypePtr, hexPtr, decodedPtr) => { const abiType = exports.__getString(abiTypePtr) const hex = exports.__getString(hexPtr) diff --git a/packages/lib-ts/src/helpers/constants.ts b/packages/lib-ts/src/helpers/constants.ts index 33b86a73..b1d84d9c 100644 --- a/packages/lib-ts/src/helpers/constants.ts +++ b/packages/lib-ts/src/helpers/constants.ts @@ -12,5 +12,4 @@ export enum ListType { DenyList = 1, } -// todo replace with correct address once it is deployed -export const MIMIC_HELPER_ADDRESS = '0x' +export const MIMIC_HELPER_ADDRESS = '0x4766EF65d66E8D2d92F3089Ee42e5705e8817FF0' diff --git a/packages/lib-ts/tests/environment.spec.ts b/packages/lib-ts/tests/environment.spec.ts new file mode 100644 index 00000000..b072ca78 --- /dev/null +++ b/packages/lib-ts/tests/environment.spec.ts @@ -0,0 +1,46 @@ +import { environment } from '../src/environment' +import { MIMIC_HELPER_ADDRESS } from '../src/helpers' +import { Address, ChainId } from '../src/types' + +import { randomSvmAddress, setContractCall, setEvmDecode, setEvmEncode } from './helpers' + +describe('environment', () => { + describe('getNativeTokenBalance', () => { + describe('when chainId is evm', () => { + const address = '0x9b6c444f5bbfe10680fb015e1a23bfc6193ae163' + const chainId = ChainId.OPTIMISM + + beforeEach(() => { + setEvmEncode('address', address, '0x0') + }) + + describe('when the evm call is an error', () => { + it('throws an error', () => { + const result = environment.getNativeTokenBalance(chainId, Address.fromHexString(address)) + expect(result.isError).toBe(true) + }) + }) + + describe('when evm call is successful', () => { + beforeEach(() => { + setContractCall(MIMIC_HELPER_ADDRESS, chainId, '0xeffd663c0x0', '0x100') + setEvmDecode('uint256', '0x100', '100') + }) + + it('returns the decoded value', () => { + const result = environment.getNativeTokenBalance(chainId, Address.fromHexString(address)) + expect(result.isError).toBe(false) + expect(result.unwrap().toString()).toBe('100') + }) + }) + }) + + describe('when chainId is solana', () => { + it('should throw an error', () => { + const result = environment.getNativeTokenBalance(ChainId.SOLANA_MAINNET, randomSvmAddress()) + expect(result.isError).toBe(true) + expect(result.error).toBe('Solana not supported') + }) + }) + }) +}) diff --git a/packages/lib-ts/tests/helpers.ts b/packages/lib-ts/tests/helpers.ts index 3c4ddbbe..466c848e 100644 --- a/packages/lib-ts/tests/helpers.ts +++ b/packages/lib-ts/tests/helpers.ts @@ -128,6 +128,8 @@ declare function _setFindProgramAddress(params: string, result: string): void export declare function setEvmDecode(abiType: string, hex: string, decoded: string): void +export declare function setEvmEncode(abiType: string, data: string, encoded: string): void + export declare function _setContext( timestamp: u64, consensusThreshold: u8, From 06c26dad44970f2cf1f63452b27db142b0a372b9 Mon Sep 17 00:00:00 2001 From: PedroAraoz Date: Mon, 26 Jan 2026 15:57:26 -0300 Subject: [PATCH 4/4] add changeset --- .changeset/clever-parents-talk.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/clever-parents-talk.md diff --git a/.changeset/clever-parents-talk.md b/.changeset/clever-parents-talk.md new file mode 100644 index 00000000..2fc7556e --- /dev/null +++ b/.changeset/clever-parents-talk.md @@ -0,0 +1,7 @@ +--- +"@mimicprotocol/lib-ts": patch +"@mimicprotocol/cli": patch +"@mimicprotocol/test-ts": patch +--- + +Add getNativeBalance