@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/tokens/Token.ts
CHANGED
|
@@ -1,191 +1,91 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { evm } from '../evm'
|
|
3
|
-
import { join, NATIVE_ADDRESS, parseCSV, Serializable, serialize } from '../helpers'
|
|
4
|
-
import { Address, ChainId, EvmDecodeParam } from '../types'
|
|
1
|
+
import { Address, ChainId } from '../types'
|
|
5
2
|
|
|
6
3
|
/**
|
|
7
4
|
* Represents a token on a blockchain network including data like symbol, decimals, and address.
|
|
8
|
-
* Supports both ERC-20 and
|
|
5
|
+
* Supports both ERC-20, SPL, and denomination tokens.
|
|
9
6
|
*/
|
|
10
|
-
export class Token
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
private _symbol: string
|
|
16
|
-
private _address: Address
|
|
17
|
-
private _chainId: ChainId
|
|
18
|
-
private _decimals: u8
|
|
19
|
-
private _timestamp: Date | null = null
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Creates a Token instance from an Address object.
|
|
23
|
-
* @param address - The contract address of the token
|
|
24
|
-
* @param chainId - The blockchain network identifier
|
|
25
|
-
* @param decimals - Number of decimal places (optional, will be queried if not provided)
|
|
26
|
-
* @param symbol - Token symbol (optional, will be queried if not provided)
|
|
27
|
-
* @param timestamp - Timestamp for historical queries (optional)
|
|
28
|
-
* @returns A new Token instance
|
|
29
|
-
*/
|
|
30
|
-
static fromAddress(
|
|
31
|
-
address: Address,
|
|
32
|
-
chainId: ChainId,
|
|
33
|
-
decimals: u8 = Token.EMPTY_DECIMALS,
|
|
34
|
-
symbol: string = Token.EMPTY_SYMBOL,
|
|
35
|
-
timestamp: Date | null = null
|
|
36
|
-
): Token {
|
|
37
|
-
return new Token(address, chainId, decimals, symbol, timestamp)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Creates a Token instance from a string address.
|
|
42
|
-
* @param address - The contract address as a hex string
|
|
43
|
-
* @param chainId - The blockchain network identifier
|
|
44
|
-
* @param decimals - Number of decimal places (optional, will be queried if not provided)
|
|
45
|
-
* @param symbol - Token symbol (optional, will be queried if not provided)
|
|
46
|
-
* @param timestamp - Timestamp for historical queries (optional)
|
|
47
|
-
* @returns A new Token instance
|
|
48
|
-
*/
|
|
49
|
-
static fromString(
|
|
50
|
-
address: string,
|
|
51
|
-
chainId: ChainId,
|
|
52
|
-
decimals: u8 = Token.EMPTY_DECIMALS,
|
|
53
|
-
symbol: string = Token.EMPTY_SYMBOL,
|
|
54
|
-
timestamp: Date | null = null
|
|
55
|
-
): Token {
|
|
56
|
-
return Token.fromAddress(Address.fromString(address), chainId, decimals, symbol, timestamp)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Creates a Token instance representing the native token of the specified chain.
|
|
61
|
-
* @param chainId - The blockchain network identifier
|
|
62
|
-
* @returns A new Token instance for the native token
|
|
63
|
-
*/
|
|
64
|
-
static native(chainId: ChainId): Token {
|
|
65
|
-
switch (chainId) {
|
|
66
|
-
case ChainId.ETHEREUM:
|
|
67
|
-
case ChainId.OPTIMISM:
|
|
68
|
-
return Token.fromString(NATIVE_ADDRESS, chainId, 18, 'ETH')
|
|
69
|
-
case ChainId.POLYGON:
|
|
70
|
-
return Token.fromString(NATIVE_ADDRESS, chainId, 18, 'POL')
|
|
71
|
-
default:
|
|
72
|
-
throw new Error(`Unsupported chainId: ${chainId}`)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Parses a serialized Token string and creates a Token instance.
|
|
78
|
-
* @param serialized - The serialized token string in format: Token(address,chainId)
|
|
79
|
-
* @returns A new Token instance parsed from the serialized data
|
|
80
|
-
*/
|
|
81
|
-
static parse(serialized: string): Token {
|
|
82
|
-
const isToken = serialized.startsWith(`${Token.SERIALIZED_PREFIX}(`) && serialized.endsWith(')')
|
|
83
|
-
if (!isToken) throw new Error('Invalid serialized token')
|
|
84
|
-
|
|
85
|
-
const elements = parseCSV(serialized.slice(Token.SERIALIZED_PREFIX.length + 1, -1))
|
|
86
|
-
const areNull = elements.some((element) => element === null)
|
|
87
|
-
if (areNull) throw new Error('Invalid serialized token')
|
|
88
|
-
|
|
89
|
-
const address = elements[0]!
|
|
90
|
-
const chainId = i32.parse(elements[1]!)
|
|
91
|
-
|
|
92
|
-
return Token.fromString(address, chainId)
|
|
93
|
-
}
|
|
7
|
+
export abstract class Token {
|
|
8
|
+
protected _symbol: string
|
|
9
|
+
protected _address: Address
|
|
10
|
+
protected _decimals: u8
|
|
94
11
|
|
|
95
12
|
/**
|
|
96
13
|
* Creates a new Token instance.
|
|
97
|
-
* @param address - The
|
|
98
|
-
* @param
|
|
99
|
-
* @param
|
|
100
|
-
* @param symbol - Token symbol (optional, will be queried if not provided)
|
|
101
|
-
* @param timestamp - Timestamp for historical queries (optional)
|
|
14
|
+
* @param address - The address of the token
|
|
15
|
+
* @param decimals - Number of decimal places
|
|
16
|
+
* @param symbol - Token symbol
|
|
102
17
|
*/
|
|
103
|
-
constructor(
|
|
104
|
-
address: Address,
|
|
105
|
-
chainId: ChainId,
|
|
106
|
-
decimals: u8 = Token.EMPTY_DECIMALS,
|
|
107
|
-
symbol: string = Token.EMPTY_SYMBOL,
|
|
108
|
-
timestamp: Date | null = null
|
|
109
|
-
) {
|
|
18
|
+
constructor(address: Address, decimals: u8, symbol: string) {
|
|
110
19
|
this._address = address
|
|
111
|
-
this._chainId = chainId
|
|
112
|
-
this._timestamp = timestamp
|
|
113
|
-
this._symbol = symbol
|
|
114
20
|
this._decimals = decimals
|
|
115
|
-
|
|
116
|
-
// Since queries return only the address and chainId, missing metadata must be filled
|
|
117
|
-
// to prevent the symbol and decimals getters from failing for native tokens.
|
|
118
|
-
if (
|
|
119
|
-
this._address.equals(Address.fromString(NATIVE_ADDRESS)) &&
|
|
120
|
-
(this._symbol === Token.EMPTY_SYMBOL || this._decimals === Token.EMPTY_DECIMALS)
|
|
121
|
-
) {
|
|
122
|
-
const nativeToken = Token.native(this._chainId)
|
|
123
|
-
if (this._symbol === Token.EMPTY_SYMBOL) {
|
|
124
|
-
this._symbol = nativeToken.symbol
|
|
125
|
-
}
|
|
126
|
-
if (this._decimals === Token.EMPTY_DECIMALS) {
|
|
127
|
-
this._decimals = nativeToken.decimals
|
|
128
|
-
}
|
|
129
|
-
}
|
|
21
|
+
this._symbol = symbol
|
|
130
22
|
}
|
|
131
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Gets the contract address of the token.
|
|
26
|
+
* The address is returned as a cloned `Address` object to ensure immutability of the internal state.
|
|
27
|
+
* @returns A cloned `Address` object representing the token’s address.
|
|
28
|
+
*/
|
|
132
29
|
get address(): Address {
|
|
133
30
|
return this._address.clone()
|
|
134
31
|
}
|
|
135
32
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Gets the token’s symbol (e.g., "ETH", "USDC").
|
|
35
|
+
* @returns A string containing the token symbol.
|
|
36
|
+
*/
|
|
140
37
|
get symbol(): string {
|
|
141
|
-
if (this._symbol === Token.EMPTY_SYMBOL) {
|
|
142
|
-
const response = environment.contractCall(this.address, this.chainId, this._timestamp, '0x95d89b41')
|
|
143
|
-
this._symbol = evm.decode(new EvmDecodeParam('string', response))
|
|
144
|
-
}
|
|
145
38
|
return this._symbol
|
|
146
39
|
}
|
|
147
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Gets the token’s decimals (number of decimal places used).
|
|
43
|
+
* @returns A `u8` representing the number of decimals of the token.
|
|
44
|
+
*/
|
|
148
45
|
get decimals(): u8 {
|
|
149
|
-
if (this._decimals == Token.EMPTY_DECIMALS) {
|
|
150
|
-
const result = environment.contractCall(this.address, this.chainId, this._timestamp, '0x313ce567')
|
|
151
|
-
this._decimals = u8.parse(evm.decode(new EvmDecodeParam('uint8', result)))
|
|
152
|
-
}
|
|
153
46
|
return this._decimals
|
|
154
47
|
}
|
|
155
48
|
|
|
156
|
-
set decimals(value: u8) {
|
|
157
|
-
this._decimals = value
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
set symbol(value: string) {
|
|
161
|
-
this._symbol = value
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
set timestamp(value: Date) {
|
|
165
|
-
this._timestamp = value
|
|
166
|
-
}
|
|
167
|
-
|
|
168
49
|
/**
|
|
169
50
|
* Checks if this token is equal to another token.
|
|
170
|
-
* Tokens are considered equal if they have the same address
|
|
51
|
+
* Tokens are considered equal if they have the same address.
|
|
171
52
|
* @param other - The token to compare with
|
|
172
|
-
* @returns True if both tokens
|
|
53
|
+
* @returns True if both tokens have the same address
|
|
173
54
|
*/
|
|
174
55
|
equals(other: Token): boolean {
|
|
175
|
-
|
|
176
|
-
const isSameAddress = this.address.equals(other.address)
|
|
177
|
-
return isSameChain && isSameAddress
|
|
56
|
+
return this.address.equals(other.address)
|
|
178
57
|
}
|
|
179
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Checks if this token is the USD denomination.
|
|
61
|
+
* @returns True if the token is the USD denomination
|
|
62
|
+
*/
|
|
63
|
+
abstract isUSD(): boolean
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Checks if this token is the native token.
|
|
67
|
+
* @returns True if the token is the native token
|
|
68
|
+
*/
|
|
69
|
+
abstract isNative(): boolean
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Checks if this token belongs to the requested chain.
|
|
73
|
+
* @param chain - The chain ID asking for
|
|
74
|
+
* @returns True if the token belongs to the requested chain
|
|
75
|
+
*/
|
|
76
|
+
abstract hasChain(chain: ChainId): boolean
|
|
77
|
+
|
|
180
78
|
/**
|
|
181
79
|
* Tells the string representation of this token.
|
|
182
|
-
* @returns The token
|
|
80
|
+
* @returns The string representation of the token
|
|
183
81
|
*/
|
|
184
|
-
toString(): string
|
|
185
|
-
|
|
186
|
-
}
|
|
82
|
+
abstract toString(): string
|
|
83
|
+
}
|
|
187
84
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
85
|
+
@json
|
|
86
|
+
export class SerializableToken {
|
|
87
|
+
constructor(
|
|
88
|
+
public address: string,
|
|
89
|
+
public chainId: i32
|
|
90
|
+
) {}
|
|
191
91
|
}
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
import { environment } from '../environment'
|
|
2
|
-
import {
|
|
3
|
-
import { BigInt } from '../types'
|
|
2
|
+
import { BigInt, JSON, Result } from '../types'
|
|
4
3
|
|
|
5
|
-
import {
|
|
4
|
+
import { BlockchainToken } from './BlockchainToken'
|
|
5
|
+
import { SerializableToken, Token } from './Token'
|
|
6
6
|
import { USD } from './USD'
|
|
7
7
|
|
|
8
|
+
const BPS_SCALE = BigInt.fromI32(10_000)
|
|
9
|
+
|
|
8
10
|
/**
|
|
9
11
|
* Represents an amount of a specific token, combining the token metadata with a quantity.
|
|
10
12
|
* Supports arithmetic operations, comparisons, and conversions between tokens and USD.
|
|
11
13
|
*/
|
|
12
|
-
export class TokenAmount
|
|
13
|
-
private static readonly SERIALIZED_PREFIX: string = 'TokenAmount'
|
|
14
|
-
|
|
14
|
+
export class TokenAmount {
|
|
15
15
|
private _token: Token
|
|
16
16
|
private _amount: BigInt
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Creates a TokenAmount instance representing zero.
|
|
20
|
+
* @returns A new TokenAmount instance with amount 0
|
|
21
|
+
*/
|
|
22
|
+
static zero(token: Token): TokenAmount {
|
|
23
|
+
return this.fromBigInt(token, BigInt.zero())
|
|
24
|
+
}
|
|
25
|
+
|
|
18
26
|
/**
|
|
19
27
|
* Creates a TokenAmount from a decimal string representation.
|
|
20
28
|
* @param token - The token to create an amount for
|
|
@@ -46,22 +54,14 @@ export class TokenAmount implements Serializable {
|
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
/**
|
|
49
|
-
*
|
|
50
|
-
* @param serialized - The serialized string
|
|
51
|
-
* @returns A new TokenAmount instance
|
|
57
|
+
* Creates a TokenAmount from a serialized string.
|
|
58
|
+
* @param serialized - The serialized string to parse
|
|
59
|
+
* @returns A new TokenAmount instance
|
|
52
60
|
*/
|
|
53
|
-
static
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const elements = parseCSV(serialized.slice(TokenAmount.SERIALIZED_PREFIX.length + 1, -1))
|
|
58
|
-
const areNull = elements.some((element) => element === null)
|
|
59
|
-
if (areNull) throw new Error('Invalid serialized token amount')
|
|
60
|
-
|
|
61
|
-
const token = Token.parse(elements[0]!)
|
|
62
|
-
const amount = BigInt.parse(elements[1]!)
|
|
63
|
-
|
|
64
|
-
return new TokenAmount(token, amount)
|
|
61
|
+
static fromSerializable(serialized: string): TokenAmount {
|
|
62
|
+
const data = JSON.parse<SerializableTokenAmount>(serialized)
|
|
63
|
+
const token = BlockchainToken.fromString(data.token.address, data.token.chainId)
|
|
64
|
+
return TokenAmount.fromStringDecimal(token, data.amount)
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
@@ -217,11 +217,14 @@ export class TokenAmount implements Serializable {
|
|
|
217
217
|
* Converts this TokenAmount to its USD equivalent.
|
|
218
218
|
* @returns A USD instance representing the current USD value
|
|
219
219
|
*/
|
|
220
|
-
toUsd(): USD {
|
|
221
|
-
if (this.isZero()) return USD.zero()
|
|
222
|
-
const
|
|
220
|
+
toUsd(): Result<USD, string> {
|
|
221
|
+
if (this.isZero()) return Result.ok<USD, string>(USD.zero())
|
|
222
|
+
const tokenPriceResult = environment.tokenPriceQuery(this.token)
|
|
223
|
+
if (tokenPriceResult.isError) return Result.err<USD, string>(tokenPriceResult.error)
|
|
224
|
+
|
|
225
|
+
const tokenPrice = tokenPriceResult.unwrap()
|
|
223
226
|
const amountUsd = this.amount.times(tokenPrice.value).downscale(this.decimals)
|
|
224
|
-
return USD.fromBigInt(amountUsd)
|
|
227
|
+
return Result.ok<USD, string>(USD.fromBigInt(amountUsd))
|
|
225
228
|
}
|
|
226
229
|
|
|
227
230
|
/**
|
|
@@ -229,13 +232,39 @@ export class TokenAmount implements Serializable {
|
|
|
229
232
|
* @param other - The target token to convert to
|
|
230
233
|
* @returns A TokenAmount of the target token with equivalent USD value
|
|
231
234
|
*/
|
|
232
|
-
toTokenAmount(other: Token): TokenAmount {
|
|
233
|
-
if (this.isZero()) return TokenAmount.fromI32(other, 0)
|
|
234
|
-
|
|
235
|
+
toTokenAmount(other: Token): Result<TokenAmount, string> {
|
|
236
|
+
if (this.isZero()) return Result.ok<TokenAmount, string>(TokenAmount.fromI32(other, 0))
|
|
237
|
+
const usdResult = this.toUsd()
|
|
238
|
+
if (usdResult.isError) return Result.err<TokenAmount, string>(usdResult.error)
|
|
239
|
+
|
|
240
|
+
return usdResult.unwrap().toTokenAmount(other)
|
|
235
241
|
}
|
|
236
242
|
|
|
237
|
-
|
|
238
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Calculates the minimum output amount using basis points (bps).
|
|
245
|
+
* Formula: minAmountOut = amountOut * (10000 - bps) / 10000
|
|
246
|
+
*
|
|
247
|
+
* @param slippage - Slippage in basis points (0-10000). Example: 50 = 0.5%
|
|
248
|
+
* @returns A new TokenAmount representing the minimum output amount
|
|
249
|
+
*/
|
|
250
|
+
applySlippageBps(slippage: i32): TokenAmount {
|
|
251
|
+
const slippageBI = BigInt.fromI32(slippage)
|
|
252
|
+
if (slippageBI.isNegative() || slippageBI.gt(BPS_SCALE))
|
|
253
|
+
throw new Error(`Slippage bps must be between 0 and ${BPS_SCALE}`)
|
|
254
|
+
return this.applySlippage(slippageBI)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Calculates the minimum output amount from a percentage string.
|
|
259
|
+
* Formula: minAmountOut = amountOut * (100 - percent) / 100
|
|
260
|
+
*
|
|
261
|
+
* @param slippage - Slippage percentage as a decimal string. Example: '0.5' = 0.5%
|
|
262
|
+
* @returns A new TokenAmount representing the minimum output amount
|
|
263
|
+
*/
|
|
264
|
+
applySlippagePercentage(slippage: string): TokenAmount {
|
|
265
|
+
const bps = BigInt.fromStringDecimal(slippage, 2)
|
|
266
|
+
if (bps.isNegative() || bps.gt(BPS_SCALE)) throw new Error('Slippage percentage must be between 0 and 100')
|
|
267
|
+
return this.applySlippage(bps)
|
|
239
268
|
}
|
|
240
269
|
|
|
241
270
|
private amountCompare(other: TokenAmount): i32 {
|
|
@@ -245,4 +274,17 @@ export class TokenAmount implements Serializable {
|
|
|
245
274
|
private checkToken(other: Token, action: string): void {
|
|
246
275
|
if (!this.token.equals(other)) throw new Error(`Cannot ${action} different tokens`)
|
|
247
276
|
}
|
|
277
|
+
|
|
278
|
+
private applySlippage(value: BigInt): TokenAmount {
|
|
279
|
+
const factor = BPS_SCALE.minus(value)
|
|
280
|
+
return this.times(factor).div(BPS_SCALE)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
@json
|
|
285
|
+
export class SerializableTokenAmount {
|
|
286
|
+
constructor(
|
|
287
|
+
public token: SerializableToken,
|
|
288
|
+
public amount: string
|
|
289
|
+
) {}
|
|
248
290
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ChainId } from '../types'
|
|
2
|
+
|
|
3
|
+
import { BlockchainToken } from './BlockchainToken'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Represents a token provider that can resolve to a specific BlockchainToken instance
|
|
7
|
+
* based on the provided chain ID.
|
|
8
|
+
* This allows for convenient access to common tokens across different chains.
|
|
9
|
+
* Supports both ERC20Token (EVM chains) and SPLToken (Solana).
|
|
10
|
+
*/
|
|
11
|
+
export class TokenProvider {
|
|
12
|
+
private _symbol: string
|
|
13
|
+
private _registry: Map<ChainId, BlockchainToken>
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new TokenProvider instance.
|
|
17
|
+
* @param symbol - The token symbol (e.g., "USDC", "USDT")
|
|
18
|
+
*/
|
|
19
|
+
constructor(symbol: string) {
|
|
20
|
+
this._symbol = symbol
|
|
21
|
+
this._registry = new Map<ChainId, BlockchainToken>()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Registers a token for a specific chain.
|
|
26
|
+
* @param chainId - The chain ID to register the token for
|
|
27
|
+
* @param token - The BlockchainToken instance for this chain (ERC20Token or SPLToken)
|
|
28
|
+
* @returns The TokenProvider instance for method chaining
|
|
29
|
+
*/
|
|
30
|
+
register(chainId: ChainId, token: BlockchainToken): TokenProvider {
|
|
31
|
+
this._registry.set(chainId, token)
|
|
32
|
+
return this
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Resolves the token provider to a specific BlockchainToken instance for the given chain.
|
|
37
|
+
* @param chainId - The chain ID to resolve the token for
|
|
38
|
+
* @returns The BlockchainToken instance for the specified chain
|
|
39
|
+
* @throws Error if the token is not supported on the requested chain
|
|
40
|
+
*/
|
|
41
|
+
on(chainId: i32): BlockchainToken {
|
|
42
|
+
if (!this.isSupported(chainId)) throw new Error(`Token ${this._symbol} is not registered on chain ${chainId}`)
|
|
43
|
+
return this._registry.get(chainId)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Checks if the token is supported on the given chain.
|
|
48
|
+
* @param chainId - The chain ID to check
|
|
49
|
+
* @returns True if the token is supported on the chain, false otherwise
|
|
50
|
+
*/
|
|
51
|
+
isSupported(chainId: i32): bool {
|
|
52
|
+
return this._registry.has(chainId)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { Arbitrum, Avalanche, Base, BaseSepolia, BNB, Ethereum, Gnosis, Optimism, Polygon, Sonic } from '../chains'
|
|
2
|
+
|
|
3
|
+
import { TokenProvider } from './TokenProvider'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Token providers that can resolve to chain-specific BlockchainToken instances.
|
|
7
|
+
* Use the `.on(chainId)` method to get the token for a specific chain.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { Tokens, Ethereum } from '@mimicprotocol/lib-ts'
|
|
12
|
+
*
|
|
13
|
+
* const usdc = Tokens.USDC.on(inputs.chainId)
|
|
14
|
+
* const weth = Tokens.WETH.on(Ethereum.CHAIN_ID)
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export class Tokens {
|
|
18
|
+
private static _instance: Tokens | null = null
|
|
19
|
+
|
|
20
|
+
private readonly usdc: TokenProvider = new TokenProvider('USDC')
|
|
21
|
+
private readonly usdt: TokenProvider = new TokenProvider('USDT')
|
|
22
|
+
private readonly dai: TokenProvider = new TokenProvider('DAI')
|
|
23
|
+
private readonly wbtc: TokenProvider = new TokenProvider('WBTC')
|
|
24
|
+
private readonly weth: TokenProvider = new TokenProvider('WETH')
|
|
25
|
+
private readonly eth: TokenProvider = new TokenProvider('ETH')
|
|
26
|
+
private readonly avax: TokenProvider = new TokenProvider('AVAX')
|
|
27
|
+
private readonly wavax: TokenProvider = new TokenProvider('WAVAX')
|
|
28
|
+
private readonly pol: TokenProvider = new TokenProvider('POL')
|
|
29
|
+
private readonly wpol: TokenProvider = new TokenProvider('WPOL')
|
|
30
|
+
private readonly bnb: TokenProvider = new TokenProvider('BNB')
|
|
31
|
+
private readonly wbnb: TokenProvider = new TokenProvider('WBNB')
|
|
32
|
+
private readonly xdai: TokenProvider = new TokenProvider('xDAI')
|
|
33
|
+
private readonly sonic: TokenProvider = new TokenProvider('SONIC')
|
|
34
|
+
private readonly wxdai: TokenProvider = new TokenProvider('WXDAI')
|
|
35
|
+
private readonly wsonic: TokenProvider = new TokenProvider('WSONIC')
|
|
36
|
+
|
|
37
|
+
private constructor() {
|
|
38
|
+
// Ethereum
|
|
39
|
+
this.usdc.register(Ethereum.CHAIN_ID, Ethereum.USDC)
|
|
40
|
+
this.usdt.register(Ethereum.CHAIN_ID, Ethereum.USDT)
|
|
41
|
+
this.dai.register(Ethereum.CHAIN_ID, Ethereum.DAI)
|
|
42
|
+
this.wbtc.register(Ethereum.CHAIN_ID, Ethereum.WBTC)
|
|
43
|
+
this.weth.register(Ethereum.CHAIN_ID, Ethereum.WETH)
|
|
44
|
+
this.eth.register(Ethereum.CHAIN_ID, Ethereum.ETH)
|
|
45
|
+
|
|
46
|
+
// Arbitrum
|
|
47
|
+
this.usdc.register(Arbitrum.CHAIN_ID, Arbitrum.USDC)
|
|
48
|
+
this.usdt.register(Arbitrum.CHAIN_ID, Arbitrum.USDT)
|
|
49
|
+
this.dai.register(Arbitrum.CHAIN_ID, Arbitrum.DAI)
|
|
50
|
+
this.wbtc.register(Arbitrum.CHAIN_ID, Arbitrum.WBTC)
|
|
51
|
+
this.weth.register(Arbitrum.CHAIN_ID, Arbitrum.WETH)
|
|
52
|
+
this.eth.register(Arbitrum.CHAIN_ID, Arbitrum.ETH)
|
|
53
|
+
|
|
54
|
+
// Avalanche
|
|
55
|
+
this.usdc.register(Avalanche.CHAIN_ID, Avalanche.USDC)
|
|
56
|
+
this.usdt.register(Avalanche.CHAIN_ID, Avalanche.USDT)
|
|
57
|
+
this.dai.register(Avalanche.CHAIN_ID, Avalanche.DAI)
|
|
58
|
+
this.wbtc.register(Avalanche.CHAIN_ID, Avalanche.WBTC)
|
|
59
|
+
this.weth.register(Avalanche.CHAIN_ID, Avalanche.WETH)
|
|
60
|
+
this.avax.register(Avalanche.CHAIN_ID, Avalanche.AVAX)
|
|
61
|
+
this.wavax.register(Avalanche.CHAIN_ID, Avalanche.WAVAX)
|
|
62
|
+
|
|
63
|
+
// Base
|
|
64
|
+
this.usdc.register(Base.CHAIN_ID, Base.USDC)
|
|
65
|
+
this.usdt.register(Base.CHAIN_ID, Base.USDT)
|
|
66
|
+
this.dai.register(Base.CHAIN_ID, Base.DAI)
|
|
67
|
+
this.wbtc.register(Base.CHAIN_ID, Base.WBTC)
|
|
68
|
+
this.weth.register(Base.CHAIN_ID, Base.WETH)
|
|
69
|
+
this.eth.register(Base.CHAIN_ID, Base.ETH)
|
|
70
|
+
|
|
71
|
+
// BNB
|
|
72
|
+
this.usdc.register(BNB.CHAIN_ID, BNB.USDC)
|
|
73
|
+
this.usdt.register(BNB.CHAIN_ID, BNB.USDT)
|
|
74
|
+
this.dai.register(BNB.CHAIN_ID, BNB.DAI)
|
|
75
|
+
this.wbtc.register(BNB.CHAIN_ID, BNB.WBTC)
|
|
76
|
+
this.weth.register(BNB.CHAIN_ID, BNB.WETH)
|
|
77
|
+
this.bnb.register(BNB.CHAIN_ID, BNB.BNB)
|
|
78
|
+
this.wbnb.register(BNB.CHAIN_ID, BNB.WBNB)
|
|
79
|
+
|
|
80
|
+
// Optimism
|
|
81
|
+
this.usdc.register(Optimism.CHAIN_ID, Optimism.USDC)
|
|
82
|
+
this.usdt.register(Optimism.CHAIN_ID, Optimism.USDT)
|
|
83
|
+
this.dai.register(Optimism.CHAIN_ID, Optimism.DAI)
|
|
84
|
+
this.wbtc.register(Optimism.CHAIN_ID, Optimism.WBTC)
|
|
85
|
+
this.weth.register(Optimism.CHAIN_ID, Optimism.WETH)
|
|
86
|
+
this.eth.register(Optimism.CHAIN_ID, Optimism.ETH)
|
|
87
|
+
|
|
88
|
+
// Gnosis
|
|
89
|
+
this.usdc.register(Gnosis.CHAIN_ID, Gnosis.USDC)
|
|
90
|
+
this.usdt.register(Gnosis.CHAIN_ID, Gnosis.USDT)
|
|
91
|
+
this.wbtc.register(Gnosis.CHAIN_ID, Gnosis.WBTC)
|
|
92
|
+
this.weth.register(Gnosis.CHAIN_ID, Gnosis.WETH)
|
|
93
|
+
this.xdai.register(Gnosis.CHAIN_ID, Gnosis.xDAI)
|
|
94
|
+
this.wxdai.register(Gnosis.CHAIN_ID, Gnosis.WXDAI)
|
|
95
|
+
|
|
96
|
+
// Polygon
|
|
97
|
+
this.usdc.register(Polygon.CHAIN_ID, Polygon.USDC)
|
|
98
|
+
this.usdt.register(Polygon.CHAIN_ID, Polygon.USDT)
|
|
99
|
+
this.dai.register(Polygon.CHAIN_ID, Polygon.DAI)
|
|
100
|
+
this.wbtc.register(Polygon.CHAIN_ID, Polygon.WBTC)
|
|
101
|
+
this.weth.register(Polygon.CHAIN_ID, Polygon.WETH)
|
|
102
|
+
this.pol.register(Polygon.CHAIN_ID, Polygon.POL)
|
|
103
|
+
this.wpol.register(Polygon.CHAIN_ID, Polygon.WPOL)
|
|
104
|
+
|
|
105
|
+
// Sonic
|
|
106
|
+
this.usdc.register(Sonic.CHAIN_ID, Sonic.USDC)
|
|
107
|
+
this.usdt.register(Sonic.CHAIN_ID, Sonic.USDT)
|
|
108
|
+
this.weth.register(Sonic.CHAIN_ID, Sonic.WETH)
|
|
109
|
+
this.sonic.register(Sonic.CHAIN_ID, Sonic.SONIC)
|
|
110
|
+
this.wsonic.register(Sonic.CHAIN_ID, Sonic.WSONIC)
|
|
111
|
+
|
|
112
|
+
// BaseSepolia
|
|
113
|
+
this.eth.register(BaseSepolia.CHAIN_ID, BaseSepolia.ETH)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private static getInstance(): Tokens {
|
|
117
|
+
if (Tokens._instance === null) {
|
|
118
|
+
Tokens._instance = new Tokens()
|
|
119
|
+
}
|
|
120
|
+
return Tokens._instance!
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static get USDC(): TokenProvider {
|
|
124
|
+
return Tokens.getInstance().usdc
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static get USDT(): TokenProvider {
|
|
128
|
+
return Tokens.getInstance().usdt
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static get DAI(): TokenProvider {
|
|
132
|
+
return Tokens.getInstance().dai
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
static get WBTC(): TokenProvider {
|
|
136
|
+
return Tokens.getInstance().wbtc
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static get WETH(): TokenProvider {
|
|
140
|
+
return Tokens.getInstance().weth
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static get ETH(): TokenProvider {
|
|
144
|
+
return Tokens.getInstance().eth
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static get AVAX(): TokenProvider {
|
|
148
|
+
return Tokens.getInstance().avax
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
static get WAVAX(): TokenProvider {
|
|
152
|
+
return Tokens.getInstance().wavax
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
static get POL(): TokenProvider {
|
|
156
|
+
return Tokens.getInstance().pol
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
static get WPOL(): TokenProvider {
|
|
160
|
+
return Tokens.getInstance().wpol
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static get BNB(): TokenProvider {
|
|
164
|
+
return Tokens.getInstance().bnb
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
static get WBNB(): TokenProvider {
|
|
168
|
+
return Tokens.getInstance().wbnb
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
static get XDAI(): TokenProvider {
|
|
172
|
+
return Tokens.getInstance().xdai
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
static get SONIC(): TokenProvider {
|
|
176
|
+
return Tokens.getInstance().sonic
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
static get WXDAI(): TokenProvider {
|
|
180
|
+
return Tokens.getInstance().wxdai
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
static get WSONIC(): TokenProvider {
|
|
184
|
+
return Tokens.getInstance().wsonic
|
|
185
|
+
}
|
|
186
|
+
}
|
package/src/tokens/USD.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { environment } from '../environment'
|
|
2
2
|
import { STANDARD_DECIMALS } from '../helpers'
|
|
3
3
|
import { Token, TokenAmount } from '../tokens'
|
|
4
|
-
import { BigInt } from '../types'
|
|
4
|
+
import { BigInt, Result } from '../types'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Represents a USD amount with fixed decimal precision.
|
|
@@ -42,7 +42,6 @@ export class USD {
|
|
|
42
42
|
* @returns A new USD instance
|
|
43
43
|
*/
|
|
44
44
|
static fromBigInt(amount: BigInt): USD {
|
|
45
|
-
if (amount.isNegative()) throw new Error('USD cannot be negative')
|
|
46
45
|
return new USD(amount)
|
|
47
46
|
}
|
|
48
47
|
|
|
@@ -51,6 +50,7 @@ export class USD {
|
|
|
51
50
|
* @param amount - The amount in 18 decimals precision (must be non-negative)
|
|
52
51
|
*/
|
|
53
52
|
constructor(amount: BigInt) {
|
|
53
|
+
if (amount.isNegative()) throw new Error('USD cannot be negative')
|
|
54
54
|
this._value = amount.clone()
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -192,10 +192,13 @@ export class USD {
|
|
|
192
192
|
* @param token - The target token to convert to
|
|
193
193
|
* @returns A TokenAmount representing the equivalent value in the target token
|
|
194
194
|
*/
|
|
195
|
-
toTokenAmount(token: Token): TokenAmount {
|
|
196
|
-
if (this.isZero()) return TokenAmount.fromI32(token, 0)
|
|
197
|
-
const
|
|
195
|
+
toTokenAmount(token: Token): Result<TokenAmount, string> {
|
|
196
|
+
if (this.isZero()) return Result.ok<TokenAmount, string>(TokenAmount.fromI32(token, 0))
|
|
197
|
+
const tokenPriceResult = environment.tokenPriceQuery(token)
|
|
198
|
+
if (tokenPriceResult.isError) return Result.err<TokenAmount, string>(tokenPriceResult.error)
|
|
199
|
+
|
|
200
|
+
const tokenPrice = tokenPriceResult.unwrap()
|
|
198
201
|
const tokenAmount = this.value.upscale(token.decimals).div(tokenPrice.value)
|
|
199
|
-
return TokenAmount.fromBigInt(token, tokenAmount)
|
|
202
|
+
return Result.ok<TokenAmount, string>(TokenAmount.fromBigInt(token, tokenAmount))
|
|
200
203
|
}
|
|
201
204
|
}
|
package/src/tokens/index.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export * from './BlockchainToken'
|
|
2
|
+
export * from './DenominationToken'
|
|
3
|
+
export * from './ERC20Token'
|
|
4
|
+
export * from './SPLToken'
|
|
1
5
|
export * from './Token'
|
|
2
6
|
export * from './TokenAmount'
|
|
7
|
+
export * from './TokenProvider'
|
|
8
|
+
export * from './Tokens'
|
|
3
9
|
export * from './USD'
|