@mimicprotocol/lib-ts 0.0.1-rc.10

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.
@@ -0,0 +1,169 @@
1
+ import { environment } from '../environment'
2
+ import { evm } from '../evm'
3
+ import { NATIVE_ADDRESS } from '../helpers'
4
+ import { Address, ChainId, EvmDecodeParam } from '../types'
5
+
6
+ /**
7
+ * Represents a token on a blockchain network including data like symbol, decimals, and address.
8
+ * Supports both ERC-20 and native tokens.
9
+ */
10
+ export class Token {
11
+ public static readonly EMPTY_DECIMALS: u8 = u8.MAX_VALUE
12
+ public static readonly EMPTY_SYMBOL: string = ''
13
+
14
+ private _symbol: string
15
+ private _address: Address
16
+ private _chainId: ChainId
17
+ private _decimals: u8
18
+ private _timestamp: Date | null = null
19
+
20
+ /**
21
+ * Creates a Token instance from an Address object.
22
+ * @param address - The contract address of the token
23
+ * @param chainId - The blockchain network identifier
24
+ * @param decimals - Number of decimal places (optional, will be queried if not provided)
25
+ * @param symbol - Token symbol (optional, will be queried if not provided)
26
+ * @param timestamp - Timestamp for historical queries (optional)
27
+ * @returns A new Token instance
28
+ */
29
+ static fromAddress(
30
+ address: Address,
31
+ chainId: ChainId,
32
+ decimals: u8 = Token.EMPTY_DECIMALS,
33
+ symbol: string = Token.EMPTY_SYMBOL,
34
+ timestamp: Date | null = null
35
+ ): Token {
36
+ return new Token(address, chainId, decimals, symbol, timestamp)
37
+ }
38
+
39
+ /**
40
+ * Creates a Token instance from a string address.
41
+ * @param address - The contract address as a hex string
42
+ * @param chainId - The blockchain network identifier
43
+ * @param decimals - Number of decimal places (optional, will be queried if not provided)
44
+ * @param symbol - Token symbol (optional, will be queried if not provided)
45
+ * @param timestamp - Timestamp for historical queries (optional)
46
+ * @returns A new Token instance
47
+ */
48
+ static fromString(
49
+ address: string,
50
+ chainId: ChainId,
51
+ decimals: u8 = Token.EMPTY_DECIMALS,
52
+ symbol: string = Token.EMPTY_SYMBOL,
53
+ timestamp: Date | null = null
54
+ ): Token {
55
+ return Token.fromAddress(Address.fromString(address), chainId, decimals, symbol, timestamp)
56
+ }
57
+
58
+ /**
59
+ * Creates a Token instance representing the native token of the specified chain.
60
+ * @param chainId - The blockchain network identifier
61
+ * @returns A new Token instance for the native token
62
+ */
63
+ static native(chainId: ChainId): Token {
64
+ switch (chainId) {
65
+ case ChainId.ETHEREUM:
66
+ case ChainId.BASE:
67
+ case ChainId.ARBITRUM:
68
+ case ChainId.OPTIMISM:
69
+ return Token.fromString(NATIVE_ADDRESS, chainId, 18, 'ETH')
70
+ case ChainId.GNOSIS:
71
+ return Token.fromString(NATIVE_ADDRESS, chainId, 18, 'xDAI')
72
+ default:
73
+ throw new Error(`Unsupported chainId: ${chainId}`)
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Creates a new Token instance.
79
+ * @param address - The contract address of the token
80
+ * @param chainId - The blockchain network identifier
81
+ * @param decimals - Number of decimal places (optional, will be queried if not provided)
82
+ * @param symbol - Token symbol (optional, will be queried if not provided)
83
+ * @param timestamp - Timestamp for historical queries (optional)
84
+ */
85
+ constructor(
86
+ address: Address,
87
+ chainId: ChainId,
88
+ decimals: u8 = Token.EMPTY_DECIMALS,
89
+ symbol: string = Token.EMPTY_SYMBOL,
90
+ timestamp: Date | null = null
91
+ ) {
92
+ this._address = address
93
+ this._chainId = chainId
94
+ this._timestamp = timestamp
95
+ this._symbol = symbol
96
+ this._decimals = decimals
97
+ // Ensure symbol and decimals are set for native tokens.
98
+ // Since queries return only the address and chainId, missing metadata must be filled
99
+ // to prevent the symbol and decimals getters from failing for native tokens.
100
+ if (
101
+ this._address.equals(Address.fromString(NATIVE_ADDRESS)) &&
102
+ (this._symbol === Token.EMPTY_SYMBOL || this._decimals === Token.EMPTY_DECIMALS)
103
+ ) {
104
+ const nativeToken = Token.native(this._chainId)
105
+ if (this._symbol === Token.EMPTY_SYMBOL) {
106
+ this._symbol = nativeToken.symbol
107
+ }
108
+ if (this._decimals === Token.EMPTY_DECIMALS) {
109
+ this._decimals = nativeToken.decimals
110
+ }
111
+ }
112
+ }
113
+
114
+ get address(): Address {
115
+ return this._address.clone()
116
+ }
117
+
118
+ get chainId(): ChainId {
119
+ return this._chainId
120
+ }
121
+
122
+ get symbol(): string {
123
+ if (this._symbol === Token.EMPTY_SYMBOL) {
124
+ const response = environment.contractCall(this.address, this.chainId, this._timestamp, '0x95d89b41')
125
+ this._symbol = evm.decode(new EvmDecodeParam('string', response))
126
+ }
127
+ return this._symbol
128
+ }
129
+
130
+ get decimals(): u8 {
131
+ if (this._decimals == Token.EMPTY_DECIMALS) {
132
+ const result = environment.contractCall(this.address, this.chainId, this._timestamp, '0x313ce567')
133
+ this._decimals = u8.parse(evm.decode(new EvmDecodeParam('uint8', result)))
134
+ }
135
+ return this._decimals
136
+ }
137
+
138
+ set decimals(value: u8) {
139
+ this._decimals = value
140
+ }
141
+
142
+ set symbol(value: string) {
143
+ this._symbol = value
144
+ }
145
+
146
+ set timestamp(value: Date) {
147
+ this._timestamp = value
148
+ }
149
+
150
+ /**
151
+ * Checks if this token is equal to another token.
152
+ * Tokens are considered equal if they have the same address on the same chain.
153
+ * @param other - The token to compare with
154
+ * @returns True if both tokens represent the same asset
155
+ */
156
+ equals(other: Token): boolean {
157
+ const isSameChain = this.chainId === other.chainId
158
+ const isSameAddress = this.address.equals(other.address)
159
+ return isSameChain && isSameAddress
160
+ }
161
+
162
+ /**
163
+ * Tells the string representation of this token.
164
+ * @returns The token symbol
165
+ */
166
+ toString(): string {
167
+ return this.symbol
168
+ }
169
+ }
@@ -0,0 +1,222 @@
1
+ import { environment } from '../environment'
2
+ import { BigInt } from '../types'
3
+
4
+ import { Token } from './Token'
5
+ import { USD } from './USD'
6
+
7
+ /**
8
+ * Represents an amount of a specific token, combining the token metadata with a quantity.
9
+ * Supports arithmetic operations, comparisons, and conversions between tokens and USD.
10
+ */
11
+ export class TokenAmount {
12
+ private _token: Token
13
+ private _amount: BigInt
14
+
15
+ /**
16
+ * Creates a TokenAmount from a decimal string representation.
17
+ * @param token - The token to create an amount for
18
+ * @param amount - The amount as a decimal string (e.g., "1.5", "100.0")
19
+ * @returns A new TokenAmount instance
20
+ */
21
+ static fromStringDecimal(token: Token, amount: string): TokenAmount {
22
+ return this.fromBigInt(token, BigInt.fromStringDecimal(amount, token.decimals))
23
+ }
24
+
25
+ /**
26
+ * Creates a TokenAmount from a 32-bit integer.
27
+ * @param token - The token to create an amount for
28
+ * @param amount - The amount as a whole number (will be scaled by token decimals)
29
+ * @returns A new TokenAmount instance
30
+ */
31
+ static fromI32(token: Token, amount: i32): TokenAmount {
32
+ return this.fromBigInt(token, BigInt.fromI32(amount).upscale(token.decimals))
33
+ }
34
+
35
+ /**
36
+ * Creates a TokenAmount from a BigInt amount.
37
+ * @param token - The token to create an amount for
38
+ * @param amount - The amount in the token's smallest unit (e.g., wei for ETH)
39
+ * @returns A new TokenAmount instance
40
+ */
41
+ static fromBigInt(token: Token, amount: BigInt): TokenAmount {
42
+ return new TokenAmount(token, amount)
43
+ }
44
+
45
+ /**
46
+ * Creates a new TokenAmount instance.
47
+ * @param token - The token this amount represents
48
+ * @param amount - The amount in the token's smallest unit (must be non-negative)
49
+ */
50
+ constructor(token: Token, amount: BigInt) {
51
+ if (amount.isNegative()) throw new Error('Token amount cannot be negative')
52
+ this._token = token
53
+ this._amount = amount.clone()
54
+ }
55
+
56
+ get token(): Token {
57
+ return this._token
58
+ }
59
+
60
+ get amount(): BigInt {
61
+ return this._amount.clone()
62
+ }
63
+
64
+ get symbol(): string {
65
+ return this.token.symbol
66
+ }
67
+
68
+ get decimals(): u8 {
69
+ return this.token.decimals
70
+ }
71
+
72
+ /**
73
+ * Checks if this token amount is zero.
74
+ * @returns True if the amount is zero
75
+ */
76
+ isZero(): boolean {
77
+ return this.amount.isZero()
78
+ }
79
+
80
+ /**
81
+ * Adds another TokenAmount to this one.
82
+ * @param other - The TokenAmount to add (must be the same token)
83
+ * @returns A new TokenAmount representing the sum
84
+ */
85
+ @operator('+')
86
+ plus(other: TokenAmount): TokenAmount {
87
+ this.checkToken(other.token, 'add')
88
+ return TokenAmount.fromBigInt(this.token, this.amount.plus(other.amount))
89
+ }
90
+
91
+ /**
92
+ * Subtracts another TokenAmount from this one.
93
+ * @param other - The TokenAmount to subtract (must be the same token)
94
+ * @returns A new TokenAmount representing the difference
95
+ */
96
+ @operator('-')
97
+ minus(other: TokenAmount): TokenAmount {
98
+ this.checkToken(other.token, 'subtract')
99
+ return TokenAmount.fromBigInt(this.token, this.amount.minus(other.amount))
100
+ }
101
+
102
+ /**
103
+ * Multiplies this TokenAmount by a BigInt factor.
104
+ * @param other - The BigInt to multiply by
105
+ * @returns A new TokenAmount representing the product
106
+ */
107
+ @operator('*')
108
+ times(other: BigInt): TokenAmount {
109
+ return TokenAmount.fromBigInt(this.token, this.amount.times(other))
110
+ }
111
+
112
+ /**
113
+ * Divides this TokenAmount by a BigInt divisor.
114
+ * @param other - The BigInt to divide by (cannot be zero)
115
+ * @returns A new TokenAmount representing the quotient
116
+ */
117
+ @operator('/')
118
+ div(other: BigInt): TokenAmount {
119
+ return TokenAmount.fromBigInt(this.token, this.amount.div(other))
120
+ }
121
+
122
+ /**
123
+ * Checks if this TokenAmount is less than another.
124
+ * @param other - The TokenAmount to compare with (must be the same token)
125
+ * @returns True if this amount is smaller
126
+ */
127
+ @operator('<')
128
+ lt(other: TokenAmount): boolean {
129
+ this.checkToken(other.token, 'lt')
130
+ return this.amountCompare(other) < 0
131
+ }
132
+
133
+ /**
134
+ * Checks if this TokenAmount is greater than another.
135
+ * @param other - The TokenAmount to compare with (must be the same token)
136
+ * @returns True if this amount is larger
137
+ */
138
+ @operator('>')
139
+ gt(other: TokenAmount): boolean {
140
+ this.checkToken(other.token, 'gt')
141
+ return this.amountCompare(other) > 0
142
+ }
143
+
144
+ /**
145
+ * Checks if this TokenAmount is less than or equal to another.
146
+ * @param other - The TokenAmount to compare with (must be the same token)
147
+ * @returns True if this amount is smaller or equal
148
+ */
149
+ @operator('<=')
150
+ le(other: TokenAmount): boolean {
151
+ this.checkToken(other.token, 'le')
152
+ return this.amountCompare(other) <= 0
153
+ }
154
+
155
+ /**
156
+ * Checks if this TokenAmount is greater than or equal to another.
157
+ * @param other - The TokenAmount to compare with (must be the same token)
158
+ * @returns True if this amount is larger or equal
159
+ */
160
+ @operator('>=')
161
+ ge(other: TokenAmount): boolean {
162
+ this.checkToken(other.token, 'ge')
163
+ return this.amountCompare(other) >= 0
164
+ }
165
+
166
+ /**
167
+ * Checks if this TokenAmount is equal to another.
168
+ * @param other - The TokenAmount to compare with
169
+ * @returns True if both represent the same token and amount
170
+ */
171
+ @operator('==')
172
+ equals(other: TokenAmount): boolean {
173
+ return this.token.equals(other.token) && this.amountCompare(other) === 0
174
+ }
175
+
176
+ /**
177
+ * Checks if this TokenAmount is not equal to another.
178
+ * @param other - The TokenAmount to compare with
179
+ * @returns True if they represent different tokens or amounts
180
+ */
181
+ @operator('!=')
182
+ notEquals(other: TokenAmount): boolean {
183
+ return !this.token.equals(other.token) || this.amountCompare(other) !== 0
184
+ }
185
+
186
+ /**
187
+ * Tells the string representation of this TokenAmount.
188
+ * @returns Formatted string showing the decimal amount and symbol (e.g., "1.5 ETH")
189
+ */
190
+ toString(): string {
191
+ return `${this.amount.toStringDecimal(this.decimals)} ${this.token.symbol}`
192
+ }
193
+
194
+ /**
195
+ * Converts this TokenAmount to its USD equivalent.
196
+ * @returns A USD instance representing the current USD value
197
+ */
198
+ toUsd(): USD {
199
+ if (this.isZero()) return USD.zero()
200
+ const tokenPrice = environment.getPrice(this.token)
201
+ const amountUsd = this.amount.times(tokenPrice.value).downscale(this.decimals)
202
+ return USD.fromBigInt(amountUsd)
203
+ }
204
+
205
+ /**
206
+ * Converts this TokenAmount to an equivalent amount of another token.
207
+ * @param other - The target token to convert to
208
+ * @returns A TokenAmount of the target token with equivalent USD value
209
+ */
210
+ toTokenAmount(other: Token): TokenAmount {
211
+ if (this.isZero()) return TokenAmount.fromI32(other, 0)
212
+ return this.toUsd().toTokenAmount(other)
213
+ }
214
+
215
+ private amountCompare(other: TokenAmount): i32 {
216
+ return BigInt.compare(this._amount, other.amount)
217
+ }
218
+
219
+ private checkToken(other: Token, action: string): void {
220
+ if (!this.token.equals(other)) throw new Error(`Cannot ${action} different tokens`)
221
+ }
222
+ }
@@ -0,0 +1,201 @@
1
+ import { environment } from '../environment'
2
+ import { STANDARD_DECIMALS } from '../helpers'
3
+ import { Token, TokenAmount } from '../tokens'
4
+ import { BigInt } from '../types'
5
+
6
+ /**
7
+ * Represents a USD amount with fixed decimal precision.
8
+ * Supports arithmetic operations, comparisons, and conversions to token amounts.
9
+ */
10
+ export class USD {
11
+ private _value: BigInt
12
+
13
+ /**
14
+ * Creates a USD instance representing zero dollars.
15
+ * @returns A new USD instance with value 0
16
+ */
17
+ static zero(): USD {
18
+ return new USD(BigInt.zero())
19
+ }
20
+
21
+ /**
22
+ * Creates a USD instance from a decimal string representation.
23
+ * @param amount - The amount as a decimal string (e.g., "1.50", "100.00")
24
+ * @returns A new USD instance
25
+ */
26
+ static fromStringDecimal(amount: string): USD {
27
+ return USD.fromBigInt(BigInt.fromStringDecimal(amount, STANDARD_DECIMALS))
28
+ }
29
+
30
+ /**
31
+ * Creates a USD instance from a 32-bit integer.
32
+ * @param amount - The amount as a whole number (e.g., 150 for $150.00)
33
+ * @returns A new USD instance
34
+ */
35
+ static fromI32(amount: i32): USD {
36
+ return USD.fromBigInt(BigInt.fromI32(amount).upscale(STANDARD_DECIMALS))
37
+ }
38
+
39
+ /**
40
+ * Creates a USD instance from a BigInt amount.
41
+ * @param amount - The amount in 18 decimals precision (must be non-negative)
42
+ * @returns A new USD instance
43
+ */
44
+ static fromBigInt(amount: BigInt): USD {
45
+ return new USD(amount)
46
+ }
47
+
48
+ /**
49
+ * Creates a new USD instance.
50
+ * @param amount - The amount in 18 decimals precision (must be non-negative)
51
+ */
52
+ constructor(amount: BigInt) {
53
+ if (amount.isNegative()) throw new Error('USD cannot be negative')
54
+ this._value = amount.clone()
55
+ }
56
+
57
+ /**
58
+ * Tells the value of this USD amount in 18 decimals precision.
59
+ * @returns A new BigInt instance representing the value
60
+ */
61
+ get value(): BigInt {
62
+ return this._value.clone()
63
+ }
64
+
65
+ /**
66
+ * Checks if this USD amount is zero.
67
+ * @returns True if the amount is zero
68
+ */
69
+ isZero(): boolean {
70
+ return this.value.isZero()
71
+ }
72
+
73
+ /**
74
+ * Adds another USD amount to this one.
75
+ * @param other - The USD amount to add
76
+ * @returns A new USD instance representing the sum
77
+ */
78
+ @operator('+')
79
+ plus(other: USD): USD {
80
+ return USD.fromBigInt(this.value.plus(other.value))
81
+ }
82
+
83
+ /**
84
+ * Subtracts another USD amount from this one.
85
+ * @param other - The USD amount to subtract
86
+ * @returns A new USD instance representing the difference
87
+ */
88
+ @operator('-')
89
+ minus(other: USD): USD {
90
+ return USD.fromBigInt(this.value.minus(other.value))
91
+ }
92
+
93
+ /**
94
+ * Multiplies this USD amount by a BigInt factor.
95
+ * @param other - The BigInt to multiply by
96
+ * @returns A new USD instance representing the product
97
+ */
98
+ @operator('*')
99
+ times(other: BigInt): USD {
100
+ return USD.fromBigInt(this.value.times(other))
101
+ }
102
+
103
+ /**
104
+ * Divides this USD amount by a BigInt divisor.
105
+ * @param other - The BigInt to divide by (cannot be zero)
106
+ * @returns A new USD instance representing the quotient
107
+ */
108
+ @operator('/')
109
+ div(other: BigInt): USD {
110
+ return USD.fromBigInt(this.value.div(other))
111
+ }
112
+
113
+ /**
114
+ * Checks if this USD amount is equal to another.
115
+ * @param other - The USD amount to compare with
116
+ * @returns True if both amounts are equal
117
+ */
118
+ @operator('==')
119
+ equals(other: USD): boolean {
120
+ return this.compare(other) === 0
121
+ }
122
+
123
+ /**
124
+ * Checks if this USD amount is not equal to another.
125
+ * @param other - The USD amount to compare with
126
+ * @returns True if the amounts are different
127
+ */
128
+ @operator('!=')
129
+ notEquals(other: USD): boolean {
130
+ return this.compare(other) !== 0
131
+ }
132
+
133
+ /**
134
+ * Checks if this USD amount is less than another.
135
+ * @param other - The USD amount to compare with
136
+ * @returns True if this amount is smaller
137
+ */
138
+ @operator('<')
139
+ lt(other: USD): boolean {
140
+ return this.compare(other) < 0
141
+ }
142
+
143
+ /**
144
+ * Checks if this USD amount is greater than another.
145
+ * @param other - The USD amount to compare with
146
+ * @returns True if this amount is larger
147
+ */
148
+ @operator('>')
149
+ gt(other: USD): boolean {
150
+ return this.compare(other) > 0
151
+ }
152
+
153
+ /**
154
+ * Checks if this USD amount is less than or equal to another.
155
+ * @param other - The USD amount to compare with
156
+ * @returns True if this amount is smaller or equal
157
+ */
158
+ @operator('<=')
159
+ le(other: USD): boolean {
160
+ return this.compare(other) <= 0
161
+ }
162
+
163
+ /**
164
+ * Checks if this USD amount is greater than or equal to another.
165
+ * @param other - The USD amount to compare with
166
+ * @returns True if this amount is larger or equal
167
+ */
168
+ @operator('>=')
169
+ ge(other: USD): boolean {
170
+ return this.compare(other) >= 0
171
+ }
172
+
173
+ /**
174
+ * Compares this USD amount with another.
175
+ * @param other - The USD amount to compare with
176
+ * @returns -1 if smaller, 0 if equal, 1 if larger
177
+ */
178
+ compare(other: USD): i32 {
179
+ return BigInt.compare(this._value, other.value)
180
+ }
181
+
182
+ /**
183
+ * Tells the string representation of this USD amount.
184
+ * @returns Decimal string representation (e.g., "1.50", "100")
185
+ */
186
+ toString(): string {
187
+ return this._value.toStringDecimal(STANDARD_DECIMALS)
188
+ }
189
+
190
+ /**
191
+ * Converts this USD amount to an equivalent token amount.
192
+ * @param token - The target token to convert to
193
+ * @returns A TokenAmount representing the equivalent value in the target token
194
+ */
195
+ toTokenAmount(token: Token): TokenAmount {
196
+ if (this.isZero()) return TokenAmount.fromI32(token, 0)
197
+ const tokenPrice = environment.getPrice(token)
198
+ const tokenAmount = this.value.upscale(token.decimals).div(tokenPrice.value)
199
+ return TokenAmount.fromBigInt(token, tokenAmount)
200
+ }
201
+ }
@@ -0,0 +1,3 @@
1
+ export * from './Token'
2
+ export * from './TokenAmount'
3
+ export * from './USD'
@@ -0,0 +1,60 @@
1
+ // eslint-disable-next-line no-secrets/no-secrets
2
+ // This file is based on code from "The Graph Tooling" (https://github.com/graphprotocol/graph-tooling/tree/7faa3098b2e6c61f09fc81b8b2d333e66b0080d1).
3
+ // Licensed under the MIT License.
4
+ // Copyright (c) 2018 Graph Protocol, Inc. and contributors.
5
+ // Modified by Mimic Protocol, 2025.
6
+
7
+ import { ByteArray } from './ByteArray'
8
+ import { Bytes } from './Bytes'
9
+
10
+ /**
11
+ * Represents an Ethereum address, a fixed-length 20-byte value.
12
+ */
13
+ export class Address extends Bytes {
14
+ /**
15
+ * Returns a zero address (20 bytes filled with zeroes).
16
+ */
17
+ static zero(): Address {
18
+ const self = new ByteArray(20)
19
+ return changetype<Address>(self)
20
+ }
21
+
22
+ /**
23
+ * Converts a string representation of an address to an Address instance.
24
+ * It is assumed that the string is a hex string.
25
+ */
26
+ static fromString(str: string): Address {
27
+ return this.fromHexString(str)
28
+ }
29
+
30
+ /**
31
+ * Converts a hex string representation of an address to an Address instance.
32
+ */
33
+ static fromHexString(str: string): Address {
34
+ const bytes = Bytes.fromHexString(str)
35
+ return this.fromBytes(bytes)
36
+ }
37
+
38
+ /**
39
+ * Converts a Bytes instance to an Address.
40
+ * Throws an error if the input is not exactly 20 bytes long.
41
+ */
42
+ static fromBytes(bytes: Bytes): Address {
43
+ if (bytes.length != 20) throw new Error(`Bytes of length ${bytes.length} can not be converted to 20 byte addresses`)
44
+ return changetype<Address>(bytes)
45
+ }
46
+
47
+ /**
48
+ * Returns the address in hexadecimal. This method is overridden to avoid
49
+ * returning the UTF-8 encoded version of the address.
50
+ */
51
+ toString(): string {
52
+ return super.toHexString()
53
+ }
54
+
55
+ clone(): Address {
56
+ const copy = new ByteArray(this.length)
57
+ copy.set(this)
58
+ return changetype<Address>(copy)
59
+ }
60
+ }