@alephium/web3 1.3.0 → 1.5.0
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/dist/alephium-web3.min.js +1 -1
- package/dist/alephium-web3.min.js.map +1 -1
- package/dist/src/address/address.js +11 -5
- package/dist/src/api/api-alephium.d.ts +2 -4
- package/dist/src/api/api-alephium.js +1 -1
- package/dist/src/codec/array-codec.d.ts +4 -12
- package/dist/src/codec/array-codec.js +15 -28
- package/dist/src/codec/asset-output-codec.d.ts +6 -11
- package/dist/src/codec/asset-output-codec.js +32 -71
- package/dist/src/codec/bigint-codec.d.ts +2 -1
- package/dist/src/codec/bigint-codec.js +14 -2
- package/dist/src/codec/bytestring-codec.d.ts +6 -11
- package/dist/src/codec/bytestring-codec.js +9 -23
- package/dist/src/codec/codec.d.ts +54 -5
- package/dist/src/codec/codec.js +112 -14
- package/dist/src/codec/compact-int-codec.d.ts +65 -44
- package/dist/src/codec/compact-int-codec.js +222 -204
- package/dist/src/codec/contract-codec.d.ts +5 -8
- package/dist/src/codec/contract-codec.js +15 -29
- package/dist/src/codec/contract-output-codec.d.ts +4 -10
- package/dist/src/codec/contract-output-codec.js +20 -40
- package/dist/src/codec/contract-output-ref-codec.d.ts +2 -8
- package/dist/src/codec/contract-output-ref-codec.js +7 -17
- package/dist/src/codec/either-codec.d.ts +8 -15
- package/dist/src/codec/either-codec.js +5 -46
- package/dist/src/codec/index.d.ts +4 -3
- package/dist/src/codec/index.js +7 -4
- package/dist/src/codec/input-codec.d.ts +4 -10
- package/dist/src/codec/input-codec.js +11 -46
- package/dist/src/codec/instr-codec.d.ts +633 -40
- package/dist/src/codec/instr-codec.js +1040 -434
- package/dist/src/codec/int-as-4bytes-codec.d.ts +7 -0
- package/dist/src/codec/{signed-int-codec.js → int-as-4bytes-codec.js} +6 -12
- package/dist/src/codec/lockup-script-codec.d.ts +23 -26
- package/dist/src/codec/lockup-script-codec.js +12 -58
- package/dist/src/codec/method-codec.d.ts +6 -18
- package/dist/src/codec/method-codec.js +20 -48
- package/dist/src/codec/option-codec.d.ts +8 -13
- package/dist/src/codec/option-codec.js +14 -32
- package/dist/src/codec/output-codec.d.ts +2 -2
- package/dist/src/codec/output-codec.js +1 -1
- package/dist/src/codec/reader.d.ts +8 -0
- package/dist/src/codec/reader.js +48 -0
- package/dist/src/codec/script-codec.d.ts +6 -14
- package/dist/src/codec/script-codec.js +6 -22
- package/dist/src/codec/signature-codec.d.ts +4 -12
- package/dist/src/codec/signature-codec.js +3 -15
- package/dist/src/codec/timestamp-codec.d.ts +8 -0
- package/dist/src/codec/timestamp-codec.js +39 -0
- package/dist/src/codec/token-codec.d.ts +3 -10
- package/dist/src/codec/token-codec.js +6 -24
- package/dist/src/codec/transaction-codec.d.ts +6 -11
- package/dist/src/codec/transaction-codec.js +24 -49
- package/dist/src/codec/unlock-script-codec.d.ts +25 -36
- package/dist/src/codec/unlock-script-codec.js +26 -147
- package/dist/src/codec/unsigned-tx-codec.d.ts +8 -14
- package/dist/src/codec/unsigned-tx-codec.js +24 -66
- package/dist/src/codec/val.d.ts +27 -0
- package/dist/src/codec/val.js +33 -0
- package/dist/src/contract/contract.d.ts +1 -0
- package/dist/src/contract/contract.js +20 -13
- package/dist/src/contract/index.d.ts +1 -0
- package/dist/src/contract/index.js +1 -0
- package/dist/src/contract/ralph.d.ts +0 -4
- package/dist/src/contract/ralph.js +50 -179
- package/dist/src/contract/script-simulator.d.ts +27 -0
- package/dist/src/contract/script-simulator.js +757 -0
- package/dist/src/exchange/exchange.js +1 -1
- package/package.json +3 -4
- package/src/address/address.ts +15 -9
- package/src/api/api-alephium.ts +2 -4
- package/src/codec/array-codec.ts +16 -34
- package/src/codec/asset-output-codec.ts +38 -83
- package/src/codec/bigint-codec.ts +16 -2
- package/src/codec/bytestring-codec.ts +10 -28
- package/src/codec/codec.ts +121 -15
- package/src/codec/compact-int-codec.ts +230 -207
- package/src/codec/contract-codec.ts +20 -33
- package/src/codec/contract-output-codec.ts +22 -48
- package/src/codec/contract-output-ref-codec.ts +6 -17
- package/src/codec/either-codec.ts +4 -53
- package/src/codec/index.ts +4 -3
- package/src/codec/input-codec.ts +14 -36
- package/src/codec/instr-codec.ts +1229 -455
- package/src/codec/{signed-int-codec.ts → int-as-4bytes-codec.ts} +6 -10
- package/src/codec/lockup-script-codec.ts +28 -76
- package/src/codec/method-codec.ts +23 -61
- package/src/codec/option-codec.ts +13 -36
- package/src/codec/output-codec.ts +2 -2
- package/src/codec/reader.ts +56 -0
- package/src/codec/script-codec.ts +9 -31
- package/src/codec/signature-codec.ts +3 -18
- package/src/codec/timestamp-codec.ts +42 -0
- package/src/codec/token-codec.ts +7 -26
- package/src/codec/transaction-codec.ts +29 -58
- package/src/codec/unlock-script-codec.ts +44 -171
- package/src/codec/unsigned-tx-codec.ts +34 -63
- package/src/codec/val.ts +40 -0
- package/src/contract/contract.ts +24 -20
- package/src/contract/index.ts +1 -0
- package/src/contract/ralph.ts +76 -172
- package/src/contract/script-simulator.ts +838 -0
- package/src/exchange/exchange.ts +1 -1
- package/dist/src/codec/long-codec.d.ts +0 -8
- package/dist/src/codec/long-codec.js +0 -55
- package/dist/src/codec/signed-int-codec.d.ts +0 -8
- package/src/codec/long-codec.ts +0 -58
|
@@ -0,0 +1,838 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2018 - 2022 The Alephium Authors
|
|
3
|
+
This file is part of the alephium project.
|
|
4
|
+
|
|
5
|
+
The library is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Lesser General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
The library is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Lesser General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Lesser General Public License
|
|
16
|
+
along with the library. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { addressFromContractId } from '../address'
|
|
20
|
+
import { Token } from '../api'
|
|
21
|
+
import { boolCodec, Method, unsignedTxCodec } from '../codec'
|
|
22
|
+
import { LockupScript, lockupScriptCodec } from '../codec/lockup-script-codec'
|
|
23
|
+
import { Script } from '../codec/script-codec'
|
|
24
|
+
import { Val, ValAddress, ValBool, ValByteVec, ValI256, ValU256 } from '../codec/val'
|
|
25
|
+
import { ALPH_TOKEN_ID } from '../constants'
|
|
26
|
+
import { binToHex, HexString, hexToBinUnsafe } from '../utils'
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Contract call extracted from a script
|
|
30
|
+
* @param contractAddress the address of the contract
|
|
31
|
+
* @param approvedAttoAlphAmount the amount of ALPH approved to the contract
|
|
32
|
+
* - undefined if no ALPH is approved
|
|
33
|
+
* - 'unknown' if the amount cannot be determined
|
|
34
|
+
* - a number if the amount is known
|
|
35
|
+
* @param approvedTokens the tokens approved to the contract
|
|
36
|
+
* - undefined if no tokens are approved
|
|
37
|
+
* - 'unknown' if the tokens cannot be determined
|
|
38
|
+
* - an array of tokens if the tokens are known
|
|
39
|
+
*/
|
|
40
|
+
export interface ContractCall {
|
|
41
|
+
contractAddress: string
|
|
42
|
+
approvedAttoAlphAmount?: bigint | 'unknown'
|
|
43
|
+
approvedTokens?: Token[] | 'unknown'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class ScriptSimulator {
|
|
47
|
+
// This function without errors is recommended for now as the simulator does not support all instructions
|
|
48
|
+
static extractContractCalls(unsignedTx: HexString): ContractCall[] {
|
|
49
|
+
try {
|
|
50
|
+
return this.extractContractCallsWithErrors(unsignedTx)
|
|
51
|
+
} catch (e) {
|
|
52
|
+
console.debug('Error extracting contract calls from script', e)
|
|
53
|
+
return []
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static extractContractCallsWithErrors(unsignedTx: HexString): ContractCall[] {
|
|
58
|
+
const unsignedTxBytes = hexToBinUnsafe(unsignedTx)
|
|
59
|
+
const decodedUnsignedTx = unsignedTxCodec.decode(unsignedTxBytes)
|
|
60
|
+
const scriptOpt = decodedUnsignedTx.statefulScript
|
|
61
|
+
switch (scriptOpt.kind) {
|
|
62
|
+
case 'Some': {
|
|
63
|
+
return this.extractContractCallsFromScript(scriptOpt.value)
|
|
64
|
+
}
|
|
65
|
+
case 'None': {
|
|
66
|
+
return []
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static extractContractCallsFromScript(script: Script): ContractCall[] {
|
|
72
|
+
const methods = script.methods
|
|
73
|
+
if (methods.length === 0) {
|
|
74
|
+
return []
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const mainMethod = methods[0]
|
|
78
|
+
return this.extractContractCallsFromMainMethod(mainMethod)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static extractContractCallsFromMainMethod(mainMethod: Method): ContractCall[] {
|
|
82
|
+
const operandStack = new Stack()
|
|
83
|
+
const localVariables = new LocalVariables()
|
|
84
|
+
const contractCalls: ContractCall[] = []
|
|
85
|
+
const callerAddress: ValAddress = {
|
|
86
|
+
kind: 'Address',
|
|
87
|
+
value: { kind: 'P2PKH', value: random32Bytes() }
|
|
88
|
+
}
|
|
89
|
+
const approved = new ApprovedAccumulator()
|
|
90
|
+
for (const instr of mainMethod.instrs) {
|
|
91
|
+
switch (instr.name) {
|
|
92
|
+
case 'ConstTrue':
|
|
93
|
+
operandStack.push({ kind: 'Bool', value: true })
|
|
94
|
+
break
|
|
95
|
+
case 'ConstFalse':
|
|
96
|
+
operandStack.push({ kind: 'Bool', value: false })
|
|
97
|
+
break
|
|
98
|
+
case 'I256Const0':
|
|
99
|
+
operandStack.push({ kind: 'I256', value: 0n })
|
|
100
|
+
break
|
|
101
|
+
case 'I256Const1':
|
|
102
|
+
operandStack.push({ kind: 'I256', value: 1n })
|
|
103
|
+
break
|
|
104
|
+
case 'I256Const2':
|
|
105
|
+
operandStack.push({ kind: 'I256', value: 2n })
|
|
106
|
+
break
|
|
107
|
+
case 'I256Const3':
|
|
108
|
+
operandStack.push({ kind: 'I256', value: 3n })
|
|
109
|
+
break
|
|
110
|
+
case 'I256Const4':
|
|
111
|
+
operandStack.push({ kind: 'I256', value: 4n })
|
|
112
|
+
break
|
|
113
|
+
case 'I256Const5':
|
|
114
|
+
operandStack.push({ kind: 'I256', value: 5n })
|
|
115
|
+
break
|
|
116
|
+
case 'I256ConstN1':
|
|
117
|
+
operandStack.push({ kind: 'I256', value: -1n })
|
|
118
|
+
break
|
|
119
|
+
case 'I256Const':
|
|
120
|
+
operandStack.push({ kind: 'I256', value: instr.value })
|
|
121
|
+
break
|
|
122
|
+
case 'U256Const0':
|
|
123
|
+
operandStack.push({ kind: 'U256', value: 0n })
|
|
124
|
+
break
|
|
125
|
+
case 'U256Const1':
|
|
126
|
+
operandStack.push({ kind: 'U256', value: 1n })
|
|
127
|
+
break
|
|
128
|
+
case 'U256Const2':
|
|
129
|
+
operandStack.push({ kind: 'U256', value: 2n })
|
|
130
|
+
break
|
|
131
|
+
case 'U256Const3':
|
|
132
|
+
operandStack.push({ kind: 'U256', value: 3n })
|
|
133
|
+
break
|
|
134
|
+
case 'U256Const4':
|
|
135
|
+
operandStack.push({ kind: 'U256', value: 4n })
|
|
136
|
+
break
|
|
137
|
+
case 'U256Const5':
|
|
138
|
+
operandStack.push({ kind: 'U256', value: 5n })
|
|
139
|
+
break
|
|
140
|
+
case 'U256Const':
|
|
141
|
+
operandStack.push({ kind: 'U256', value: instr.value })
|
|
142
|
+
break
|
|
143
|
+
case 'BytesConst':
|
|
144
|
+
operandStack.push({ kind: 'ByteVec', value: instr.value })
|
|
145
|
+
break
|
|
146
|
+
case 'AddressConst':
|
|
147
|
+
operandStack.push({ kind: 'Address', value: instr.value })
|
|
148
|
+
break
|
|
149
|
+
case 'LoadLocal':
|
|
150
|
+
operandStack.push(localVariables.get(instr.index))
|
|
151
|
+
break
|
|
152
|
+
case 'StoreLocal':
|
|
153
|
+
localVariables.set(instr.index, operandStack.pop())
|
|
154
|
+
break
|
|
155
|
+
case 'Pop':
|
|
156
|
+
operandStack.pop()
|
|
157
|
+
break
|
|
158
|
+
case 'Dup':
|
|
159
|
+
const val = operandStack.pop()
|
|
160
|
+
operandStack.push(val)
|
|
161
|
+
operandStack.push(val)
|
|
162
|
+
break
|
|
163
|
+
case 'Swap':
|
|
164
|
+
const val1 = operandStack.pop()
|
|
165
|
+
const val2 = operandStack.pop()
|
|
166
|
+
operandStack.push(val1)
|
|
167
|
+
operandStack.push(val2)
|
|
168
|
+
break
|
|
169
|
+
case 'BoolNot':
|
|
170
|
+
const bool = operandStack.popBool()
|
|
171
|
+
const result = unaryOp<'Bool', boolean>(bool, (x) => !x)
|
|
172
|
+
operandStack.push(result)
|
|
173
|
+
case 'BoolAnd': {
|
|
174
|
+
const bool1 = operandStack.popBool()
|
|
175
|
+
const bool2 = operandStack.popBool()
|
|
176
|
+
binaryOp<'Bool', boolean>(bool1, bool2, (x, y) => x && y, operandStack.push)
|
|
177
|
+
break
|
|
178
|
+
}
|
|
179
|
+
case 'BoolOr': {
|
|
180
|
+
const bool1 = operandStack.popBool()
|
|
181
|
+
const bool2 = operandStack.popBool()
|
|
182
|
+
binaryOp<'Bool', boolean>(bool1, bool2, (x, y) => x || y, operandStack.push)
|
|
183
|
+
break
|
|
184
|
+
}
|
|
185
|
+
case 'BoolEq': {
|
|
186
|
+
const bool1 = operandStack.popBool()
|
|
187
|
+
const bool2 = operandStack.popBool()
|
|
188
|
+
binaryOp<'Bool', boolean>(bool1, bool2, (x, y) => x === y, operandStack.push)
|
|
189
|
+
break
|
|
190
|
+
}
|
|
191
|
+
case 'BoolNeq': {
|
|
192
|
+
const bool1 = operandStack.popBool()
|
|
193
|
+
const bool2 = operandStack.popBool()
|
|
194
|
+
binaryOp<'Bool', boolean>(bool1, bool2, (x, y) => x !== y, operandStack.push)
|
|
195
|
+
break
|
|
196
|
+
}
|
|
197
|
+
case 'BoolToByteVec': {
|
|
198
|
+
const bool = operandStack.popBool()
|
|
199
|
+
if (bool.kind === 'Symbol-Bool') {
|
|
200
|
+
operandStack.push(bool)
|
|
201
|
+
} else {
|
|
202
|
+
operandStack.push({ kind: 'ByteVec', value: boolCodec.encode(bool.value) })
|
|
203
|
+
}
|
|
204
|
+
break
|
|
205
|
+
}
|
|
206
|
+
case 'I256Add': {
|
|
207
|
+
// unsafe
|
|
208
|
+
const i256_2 = operandStack.popI256()
|
|
209
|
+
const i256_1 = operandStack.popI256()
|
|
210
|
+
binaryOp<'I256', bigint>(i256_1, i256_2, (x, y) => x + y, operandStack.push)
|
|
211
|
+
break
|
|
212
|
+
}
|
|
213
|
+
case 'I256Sub': {
|
|
214
|
+
// unsafe
|
|
215
|
+
const i256_2 = operandStack.popI256()
|
|
216
|
+
const i256_1 = operandStack.popI256()
|
|
217
|
+
binaryOp<'I256', bigint>(i256_1, i256_2, (x, y) => x - y, operandStack.push)
|
|
218
|
+
break
|
|
219
|
+
}
|
|
220
|
+
case 'I256Mul': {
|
|
221
|
+
// unsafe
|
|
222
|
+
const i256_2 = operandStack.popI256()
|
|
223
|
+
const i256_1 = operandStack.popI256()
|
|
224
|
+
binaryOp<'I256', bigint>(i256_1, i256_2, (x, y) => x * y, operandStack.push)
|
|
225
|
+
break
|
|
226
|
+
}
|
|
227
|
+
case 'I256Div': {
|
|
228
|
+
// unsafe
|
|
229
|
+
const i256_2 = operandStack.popI256()
|
|
230
|
+
const i256_1 = operandStack.popI256()
|
|
231
|
+
binaryOp<'I256', bigint>(i256_1, i256_2, (x, y) => x / y, operandStack.push)
|
|
232
|
+
break
|
|
233
|
+
}
|
|
234
|
+
case 'I256Eq': {
|
|
235
|
+
// unsafe
|
|
236
|
+
const i256_2 = operandStack.popI256()
|
|
237
|
+
const i256_1 = operandStack.popI256()
|
|
238
|
+
comparisonOp<'I256', bigint>(i256_1, i256_2, (x, y) => x === y, operandStack.push)
|
|
239
|
+
break
|
|
240
|
+
}
|
|
241
|
+
case 'I256Neq': {
|
|
242
|
+
// unsafe
|
|
243
|
+
const i256_2 = operandStack.popI256()
|
|
244
|
+
const i256_1 = operandStack.popI256()
|
|
245
|
+
comparisonOp<'I256', bigint>(i256_1, i256_2, (x, y) => x !== y, operandStack.push)
|
|
246
|
+
break
|
|
247
|
+
}
|
|
248
|
+
case 'I256Lt': {
|
|
249
|
+
// unsafe
|
|
250
|
+
const i256_2 = operandStack.popI256()
|
|
251
|
+
const i256_1 = operandStack.popI256()
|
|
252
|
+
comparisonOp<'I256', bigint>(i256_1, i256_2, (x, y) => x < y, operandStack.push)
|
|
253
|
+
break
|
|
254
|
+
}
|
|
255
|
+
case 'I256Le': {
|
|
256
|
+
// unsafe
|
|
257
|
+
const i256_2 = operandStack.popI256()
|
|
258
|
+
const i256_1 = operandStack.popI256()
|
|
259
|
+
comparisonOp<'I256', bigint>(i256_1, i256_2, (x, y) => x <= y, operandStack.push)
|
|
260
|
+
break
|
|
261
|
+
}
|
|
262
|
+
case 'I256Gt': {
|
|
263
|
+
// unsafe
|
|
264
|
+
const i256_2 = operandStack.popI256()
|
|
265
|
+
const i256_1 = operandStack.popI256()
|
|
266
|
+
comparisonOp<'I256', bigint>(i256_1, i256_2, (x, y) => x > y, operandStack.push)
|
|
267
|
+
break
|
|
268
|
+
}
|
|
269
|
+
case 'I256Ge': {
|
|
270
|
+
// unsafe
|
|
271
|
+
const i256_2 = operandStack.popI256()
|
|
272
|
+
const i256_1 = operandStack.popI256()
|
|
273
|
+
comparisonOp<'I256', bigint>(i256_1, i256_2, (x, y) => x >= y, operandStack.push)
|
|
274
|
+
break
|
|
275
|
+
}
|
|
276
|
+
case 'U256Add': {
|
|
277
|
+
// unsafe
|
|
278
|
+
const u256_2 = operandStack.popU256()
|
|
279
|
+
const u256_1 = operandStack.popU256()
|
|
280
|
+
binaryOp<'U256', bigint>(u256_1, u256_2, (x, y) => x + y, operandStack.push)
|
|
281
|
+
break
|
|
282
|
+
}
|
|
283
|
+
case 'U256Sub': {
|
|
284
|
+
// unsafe
|
|
285
|
+
const u256_2 = operandStack.popU256()
|
|
286
|
+
const u256_1 = operandStack.popU256()
|
|
287
|
+
binaryOp<'U256', bigint>(u256_1, u256_2, (x, y) => x - y, operandStack.push)
|
|
288
|
+
break
|
|
289
|
+
}
|
|
290
|
+
case 'U256Mul': {
|
|
291
|
+
// unsafe
|
|
292
|
+
const u256_2 = operandStack.popU256()
|
|
293
|
+
const u256_1 = operandStack.popU256()
|
|
294
|
+
binaryOp<'U256', bigint>(u256_1, u256_2, (x, y) => x * y, operandStack.push)
|
|
295
|
+
break
|
|
296
|
+
}
|
|
297
|
+
case 'U256Div': {
|
|
298
|
+
// unsafe
|
|
299
|
+
const u256_2 = operandStack.popU256()
|
|
300
|
+
const u256_1 = operandStack.popU256()
|
|
301
|
+
binaryOp<'U256', bigint>(u256_1, u256_2, (x, y) => x / y, operandStack.push)
|
|
302
|
+
break
|
|
303
|
+
}
|
|
304
|
+
case 'U256Eq': {
|
|
305
|
+
// unsafe
|
|
306
|
+
const u256_2 = operandStack.popU256()
|
|
307
|
+
const u256_1 = operandStack.popU256()
|
|
308
|
+
comparisonOp<'U256', bigint>(u256_1, u256_2, (x, y) => x === y, operandStack.push)
|
|
309
|
+
break
|
|
310
|
+
}
|
|
311
|
+
case 'U256Neq': {
|
|
312
|
+
// unsafe
|
|
313
|
+
const u256_2 = operandStack.popU256()
|
|
314
|
+
const u256_1 = operandStack.popU256()
|
|
315
|
+
comparisonOp<'U256', bigint>(u256_1, u256_2, (x, y) => x !== y, operandStack.push)
|
|
316
|
+
break
|
|
317
|
+
}
|
|
318
|
+
case 'U256Lt': {
|
|
319
|
+
// unsafe
|
|
320
|
+
const u256_2 = operandStack.popU256()
|
|
321
|
+
const u256_1 = operandStack.popU256()
|
|
322
|
+
comparisonOp<'U256', bigint>(u256_1, u256_2, (x, y) => x < y, operandStack.push)
|
|
323
|
+
break
|
|
324
|
+
}
|
|
325
|
+
case 'U256Le': {
|
|
326
|
+
// unsafe
|
|
327
|
+
const u256_2 = operandStack.popU256()
|
|
328
|
+
const u256_1 = operandStack.popU256()
|
|
329
|
+
comparisonOp<'U256', bigint>(u256_1, u256_2, (x, y) => x <= y, operandStack.push)
|
|
330
|
+
break
|
|
331
|
+
}
|
|
332
|
+
case 'U256Gt': {
|
|
333
|
+
// unsafe
|
|
334
|
+
const u256_2 = operandStack.popU256()
|
|
335
|
+
const u256_1 = operandStack.popU256()
|
|
336
|
+
comparisonOp<'U256', bigint>(u256_1, u256_2, (x, y) => x > y, operandStack.push)
|
|
337
|
+
break
|
|
338
|
+
}
|
|
339
|
+
case 'U256Ge': {
|
|
340
|
+
// unsafe
|
|
341
|
+
const u256_2 = operandStack.popU256()
|
|
342
|
+
const u256_1 = operandStack.popU256()
|
|
343
|
+
comparisonOp<'U256', bigint>(u256_1, u256_2, (x, y) => x >= y, operandStack.push)
|
|
344
|
+
break
|
|
345
|
+
}
|
|
346
|
+
case 'ByteVecEq': {
|
|
347
|
+
const byteVec1 = operandStack.popByteVec()
|
|
348
|
+
const byteVec2 = operandStack.popByteVec()
|
|
349
|
+
comparisonOp<'ByteVec', Uint8Array>(byteVec1, byteVec2, (x, y) => arrayEquals(x, y), operandStack.push)
|
|
350
|
+
break
|
|
351
|
+
}
|
|
352
|
+
case 'ByteVecNeq': {
|
|
353
|
+
const byteVec1 = operandStack.popByteVec()
|
|
354
|
+
const byteVec2 = operandStack.popByteVec()
|
|
355
|
+
comparisonOp<'ByteVec', Uint8Array>(byteVec1, byteVec2, (x, y) => !arrayEquals(x, y), operandStack.push)
|
|
356
|
+
break
|
|
357
|
+
}
|
|
358
|
+
case 'ByteVecSize': {
|
|
359
|
+
const byteVec = operandStack.popByteVec()
|
|
360
|
+
if (byteVec.kind === 'Symbol-ByteVec') {
|
|
361
|
+
operandStack.push({ kind: 'Symbol-U256', value: undefined })
|
|
362
|
+
} else {
|
|
363
|
+
operandStack.push({ kind: 'U256', value: BigInt(byteVec.value.length) })
|
|
364
|
+
}
|
|
365
|
+
break
|
|
366
|
+
}
|
|
367
|
+
case 'ByteVecConcat': {
|
|
368
|
+
const byteVec2 = operandStack.popByteVec()
|
|
369
|
+
const byteVec1 = operandStack.popByteVec()
|
|
370
|
+
binaryOp<'ByteVec', Uint8Array>(byteVec1, byteVec2, (x, y) => new Uint8Array([...x, ...y]), operandStack.push)
|
|
371
|
+
break
|
|
372
|
+
}
|
|
373
|
+
case 'ByteVecSlice': {
|
|
374
|
+
const end = operandStack.popU256()
|
|
375
|
+
const start = operandStack.popU256()
|
|
376
|
+
const byteVec = operandStack.popByteVec()
|
|
377
|
+
if (byteVec.kind === 'Symbol-ByteVec' || start.kind === 'Symbol-U256' || end.kind === 'Symbol-U256') {
|
|
378
|
+
operandStack.push({ kind: 'Symbol-ByteVec', value: undefined })
|
|
379
|
+
} else {
|
|
380
|
+
operandStack.push({
|
|
381
|
+
kind: 'ByteVec',
|
|
382
|
+
value: byteVec.value.slice(Number(start.value), Number(end.value))
|
|
383
|
+
})
|
|
384
|
+
}
|
|
385
|
+
break
|
|
386
|
+
}
|
|
387
|
+
case 'AddressEq': {
|
|
388
|
+
const address1 = operandStack.popAddress()
|
|
389
|
+
const address2 = operandStack.popAddress()
|
|
390
|
+
comparisonOp<'Address', LockupScript>(
|
|
391
|
+
address1,
|
|
392
|
+
address2,
|
|
393
|
+
(x, y) => arrayEquals(lockupScriptCodec.encode(x), lockupScriptCodec.encode(y)),
|
|
394
|
+
operandStack.push
|
|
395
|
+
)
|
|
396
|
+
break
|
|
397
|
+
}
|
|
398
|
+
case 'AddressNeq': {
|
|
399
|
+
const address1 = operandStack.popAddress()
|
|
400
|
+
const address2 = operandStack.popAddress()
|
|
401
|
+
comparisonOp<'Address', LockupScript>(
|
|
402
|
+
address1,
|
|
403
|
+
address2,
|
|
404
|
+
(x, y) => !arrayEquals(lockupScriptCodec.encode(x), lockupScriptCodec.encode(y)),
|
|
405
|
+
operandStack.push
|
|
406
|
+
)
|
|
407
|
+
break
|
|
408
|
+
}
|
|
409
|
+
case 'AddressToByteVec': {
|
|
410
|
+
const address = operandStack.popAddress()
|
|
411
|
+
if (address.kind === 'Symbol-Address') {
|
|
412
|
+
operandStack.push({ kind: 'Symbol-ByteVec', value: undefined })
|
|
413
|
+
} else {
|
|
414
|
+
operandStack.push({ kind: 'ByteVec', value: lockupScriptCodec.encode(address.value) })
|
|
415
|
+
}
|
|
416
|
+
break
|
|
417
|
+
}
|
|
418
|
+
case 'Assert': {
|
|
419
|
+
const bool = operandStack.popBool()
|
|
420
|
+
if (!bool) {
|
|
421
|
+
throw new Error('Assertion failed')
|
|
422
|
+
}
|
|
423
|
+
break
|
|
424
|
+
}
|
|
425
|
+
case 'Blake2b':
|
|
426
|
+
case 'Sha256':
|
|
427
|
+
case 'Sha3':
|
|
428
|
+
case 'Keccak256': {
|
|
429
|
+
dummyImplementation(instr.name)
|
|
430
|
+
operandStack.popByteVec()
|
|
431
|
+
operandStack.push({ kind: 'ByteVec', value: new Uint8Array(32) })
|
|
432
|
+
break
|
|
433
|
+
}
|
|
434
|
+
case 'ByteVecToAddress': {
|
|
435
|
+
const byteVec = operandStack.popByteVec()
|
|
436
|
+
if (byteVec.kind === 'Symbol-ByteVec') {
|
|
437
|
+
operandStack.push({ kind: 'Symbol-Address', value: undefined })
|
|
438
|
+
} else {
|
|
439
|
+
operandStack.push({ kind: 'Address', value: lockupScriptCodec.decode(byteVec.value) })
|
|
440
|
+
}
|
|
441
|
+
break
|
|
442
|
+
}
|
|
443
|
+
case 'Zeros': {
|
|
444
|
+
const size = operandStack.popU256()
|
|
445
|
+
if (size.kind === 'Symbol-U256') {
|
|
446
|
+
operandStack.push({ kind: 'Symbol-ByteVec', value: undefined })
|
|
447
|
+
} else {
|
|
448
|
+
if (size.value > 4096) {
|
|
449
|
+
throw new Error('Zeros size is too large')
|
|
450
|
+
}
|
|
451
|
+
operandStack.push({ kind: 'ByteVec', value: new Uint8Array(Number(size.value)) })
|
|
452
|
+
}
|
|
453
|
+
break
|
|
454
|
+
}
|
|
455
|
+
case 'U256To1Byte':
|
|
456
|
+
case 'U256To2Byte':
|
|
457
|
+
case 'U256To4Byte':
|
|
458
|
+
case 'U256To8Byte':
|
|
459
|
+
case 'U256To16Byte':
|
|
460
|
+
case 'U256To32Byte': {
|
|
461
|
+
dummyImplementation(instr.name)
|
|
462
|
+
operandStack.popU256()
|
|
463
|
+
operandStack.push({ kind: 'Symbol-ByteVec', value: undefined })
|
|
464
|
+
break
|
|
465
|
+
}
|
|
466
|
+
case 'U256From1Byte':
|
|
467
|
+
case 'U256From2Byte':
|
|
468
|
+
case 'U256From4Byte':
|
|
469
|
+
case 'U256From8Byte':
|
|
470
|
+
case 'U256From16Byte':
|
|
471
|
+
case 'U256From32Byte': {
|
|
472
|
+
dummyImplementation(instr.name)
|
|
473
|
+
operandStack.popByteVec()
|
|
474
|
+
operandStack.push({ kind: 'Symbol-U256', value: undefined })
|
|
475
|
+
break
|
|
476
|
+
}
|
|
477
|
+
case 'CallExternal':
|
|
478
|
+
case 'CallExternalBySelector': {
|
|
479
|
+
const contractId = operandStack.popByteVec()
|
|
480
|
+
const returnLength = operandStack.popU256() // method return length
|
|
481
|
+
operandStack.popU256() // method args length
|
|
482
|
+
|
|
483
|
+
if (contractId.kind !== 'Symbol-ByteVec') {
|
|
484
|
+
contractCalls.push({
|
|
485
|
+
contractAddress: addressFromContractId(binToHex(contractId.value)),
|
|
486
|
+
approvedAttoAlphAmount: approved.getApprovedAttoAlph(),
|
|
487
|
+
approvedTokens: approved.getApprovedTokens()
|
|
488
|
+
})
|
|
489
|
+
}
|
|
490
|
+
approved.reset()
|
|
491
|
+
|
|
492
|
+
if (returnLength.kind !== 'Symbol-U256') {
|
|
493
|
+
for (let i = 0; i < returnLength.value; i++) {
|
|
494
|
+
operandStack.push({ kind: 'Symbol-Any', value: undefined })
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
break
|
|
498
|
+
}
|
|
499
|
+
case 'ContractIdToAddress': {
|
|
500
|
+
const contractId = operandStack.popByteVec()
|
|
501
|
+
if (contractId.kind === 'Symbol-ByteVec') {
|
|
502
|
+
operandStack.push({ kind: 'Symbol-Address', value: undefined })
|
|
503
|
+
} else {
|
|
504
|
+
operandStack.push({ kind: 'Address', value: { kind: 'P2C', value: contractId.value } })
|
|
505
|
+
}
|
|
506
|
+
break
|
|
507
|
+
}
|
|
508
|
+
case 'LoadLocalByIndex': {
|
|
509
|
+
const index = operandStack.popU256()
|
|
510
|
+
if (index.kind === 'Symbol-U256') {
|
|
511
|
+
throw new Error('LoadLocalByIndex index is a symbol')
|
|
512
|
+
} else {
|
|
513
|
+
operandStack.push(localVariables.get(Number(index.value)))
|
|
514
|
+
}
|
|
515
|
+
break
|
|
516
|
+
}
|
|
517
|
+
case 'StoreLocalByIndex': {
|
|
518
|
+
const index = operandStack.popU256()
|
|
519
|
+
if (index.kind === 'Symbol-U256') {
|
|
520
|
+
throw new Error('StoreLocalByIndex index is a symbol')
|
|
521
|
+
} else {
|
|
522
|
+
localVariables.set(Number(index.value), operandStack.pop())
|
|
523
|
+
}
|
|
524
|
+
break
|
|
525
|
+
}
|
|
526
|
+
case 'CallerAddress': {
|
|
527
|
+
operandStack.push(callerAddress)
|
|
528
|
+
break
|
|
529
|
+
}
|
|
530
|
+
case 'ApproveAlph': {
|
|
531
|
+
const amount = operandStack.popU256() // amount
|
|
532
|
+
const spender = operandStack.popAddress() // spender
|
|
533
|
+
if (spender.kind.startsWith('Symbol')) {
|
|
534
|
+
approved.setUnknown() // The spender might be the caller
|
|
535
|
+
} else if (spender === callerAddress) {
|
|
536
|
+
approved.addApprovedAttoAlph(amount)
|
|
537
|
+
}
|
|
538
|
+
break
|
|
539
|
+
}
|
|
540
|
+
case 'ApproveToken': {
|
|
541
|
+
const amount = operandStack.popU256() // amount
|
|
542
|
+
const tokenId = operandStack.popByteVec() // token
|
|
543
|
+
const spender = operandStack.popAddress() // spender
|
|
544
|
+
if (spender.kind.startsWith('Symbol')) {
|
|
545
|
+
approved.setUnknown() // The spender might be the caller
|
|
546
|
+
} else if (spender === callerAddress) {
|
|
547
|
+
approved.addApprovedToken(tokenId, amount)
|
|
548
|
+
}
|
|
549
|
+
break
|
|
550
|
+
}
|
|
551
|
+
case 'CreateContractAndTransferToken': {
|
|
552
|
+
operandStack.popAddress() // token owner
|
|
553
|
+
}
|
|
554
|
+
case 'CreateContractWithToken': {
|
|
555
|
+
operandStack.popU256() // token amount
|
|
556
|
+
}
|
|
557
|
+
case 'CreateContract': {
|
|
558
|
+
operandStack.popByteVec() // mutable fields
|
|
559
|
+
operandStack.popByteVec() // immutable fields
|
|
560
|
+
operandStack.popByteVec() // contract code
|
|
561
|
+
operandStack.push({ kind: 'Symbol-ByteVec', value: undefined }) // new contract id
|
|
562
|
+
break
|
|
563
|
+
}
|
|
564
|
+
case 'TransferAlph': {
|
|
565
|
+
operandStack.popU256() // amount
|
|
566
|
+
operandStack.popAddress() // recipient
|
|
567
|
+
operandStack.popAddress() // sender
|
|
568
|
+
break
|
|
569
|
+
}
|
|
570
|
+
case 'TransferToken': {
|
|
571
|
+
operandStack.popU256() // amount
|
|
572
|
+
operandStack.popByteVec() // token
|
|
573
|
+
operandStack.popAddress() // recipient
|
|
574
|
+
operandStack.popAddress() // sender
|
|
575
|
+
break
|
|
576
|
+
}
|
|
577
|
+
default:
|
|
578
|
+
unimplemented(instr.name)
|
|
579
|
+
break
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return contractCalls
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
type SymbolBool = { kind: 'Symbol-Bool'; value: undefined }
|
|
587
|
+
type SymbolI256 = { kind: 'Symbol-I256'; value: undefined }
|
|
588
|
+
type SymbolU256 = { kind: 'Symbol-U256'; value: undefined }
|
|
589
|
+
type SymbolByteVec = { kind: 'Symbol-ByteVec'; value: undefined }
|
|
590
|
+
type SymbolAddress = { kind: 'Symbol-Address'; value: undefined }
|
|
591
|
+
type SymbolAny = { kind: 'Symbol-Any'; value: undefined }
|
|
592
|
+
type SimulatorVal = Val | SymbolBool | SymbolI256 | SymbolU256 | SymbolByteVec | SymbolAddress | SymbolAny
|
|
593
|
+
type SimulatorVar<K extends string, V> = { kind: K; value: V } | { kind: `Symbol-${K}`; value: undefined }
|
|
594
|
+
|
|
595
|
+
function unaryOp<K extends string, V>(x: SimulatorVar<K, V>, op: (x: V) => V): SimulatorVar<K, V> {
|
|
596
|
+
if (x.kind.startsWith('Symbol')) {
|
|
597
|
+
return x
|
|
598
|
+
} else {
|
|
599
|
+
return { kind: x.kind as K, value: op(x.value as V) }
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function binaryOp<K extends string, V>(
|
|
604
|
+
x: SimulatorVar<K, V>,
|
|
605
|
+
y: SimulatorVar<K, V>,
|
|
606
|
+
op: (x: V, y: V) => V,
|
|
607
|
+
push: (z: SimulatorVar<K, V>) => void
|
|
608
|
+
): void {
|
|
609
|
+
const result = x.kind.startsWith('Symbol')
|
|
610
|
+
? x
|
|
611
|
+
: y.kind.startsWith('Symbol')
|
|
612
|
+
? y
|
|
613
|
+
: { kind: x.kind as K, value: op(x.value as V, y.value as V) }
|
|
614
|
+
push(result)
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function comparisonOp<K extends string, V>(
|
|
618
|
+
x: SimulatorVar<K, V>,
|
|
619
|
+
y: SimulatorVar<K, V>,
|
|
620
|
+
op: (x: V, y: V) => boolean,
|
|
621
|
+
push: (z: SimulatorVar<'Bool', boolean>) => void
|
|
622
|
+
): void {
|
|
623
|
+
const result: SimulatorVar<'Bool', boolean> =
|
|
624
|
+
x.kind.startsWith('Symbol') || y.kind.startsWith('Symbol')
|
|
625
|
+
? { kind: 'Symbol-Bool', value: undefined }
|
|
626
|
+
: { kind: 'Bool', value: op(x.value as V, y.value as V) }
|
|
627
|
+
push(result)
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// implement arrayEquals
|
|
631
|
+
function arrayEquals(x: Uint8Array, y: Uint8Array): boolean {
|
|
632
|
+
return x.length === y.length && x.every((value, index) => value === y[`${index}`])
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// generate 32 bytes array with random numbers
|
|
636
|
+
function random32Bytes(): Uint8Array {
|
|
637
|
+
const result = new Uint8Array(32)
|
|
638
|
+
for (let i = 0; i < 32; i++) {
|
|
639
|
+
result[`${i}`] = Math.floor(Math.random() * 256)
|
|
640
|
+
}
|
|
641
|
+
return result
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
class Stack {
|
|
645
|
+
private stack: SimulatorVal[] = []
|
|
646
|
+
|
|
647
|
+
constructor() {
|
|
648
|
+
// TODO
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
push = (val: SimulatorVal) => {
|
|
652
|
+
this.stack.push(val)
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
pop(): SimulatorVal {
|
|
656
|
+
const result = this.stack.pop()
|
|
657
|
+
if (result === undefined) {
|
|
658
|
+
throw new Error('Stack is empty')
|
|
659
|
+
}
|
|
660
|
+
return result
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
size(): number {
|
|
664
|
+
return this.stack.length
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
checkedResult(result: SimulatorVal, expected: string): SimulatorVal {
|
|
668
|
+
if (result.kind.startsWith('Symbol')) {
|
|
669
|
+
if (result.kind !== `Symbol-${expected}`) {
|
|
670
|
+
throw new Error(`Expected a ${expected} value on the stack`)
|
|
671
|
+
}
|
|
672
|
+
return result
|
|
673
|
+
}
|
|
674
|
+
if (result.kind !== expected) {
|
|
675
|
+
throw new Error(`Expected a ${expected} value on the stack`)
|
|
676
|
+
}
|
|
677
|
+
return result
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
popBool(): ValBool | SymbolBool {
|
|
681
|
+
const result = this.pop()
|
|
682
|
+
return this.checkedResult(result, 'Bool') as ValBool | SymbolBool
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
popI256(): ValI256 | SymbolI256 {
|
|
686
|
+
const result = this.pop()
|
|
687
|
+
return this.checkedResult(result, 'I256') as ValI256 | SymbolI256
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
popU256(): ValU256 | SymbolU256 {
|
|
691
|
+
const result = this.pop()
|
|
692
|
+
return this.checkedResult(result, 'U256') as ValU256 | SymbolU256
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
popByteVec(): ValByteVec | SymbolByteVec {
|
|
696
|
+
const result = this.pop()
|
|
697
|
+
return this.checkedResult(result, 'ByteVec') as ValByteVec | SymbolByteVec
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
popAddress(): ValAddress | SymbolAddress {
|
|
701
|
+
const result = this.pop()
|
|
702
|
+
return this.checkedResult(result, 'Address') as ValAddress | SymbolAddress
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
class LocalVariables {
|
|
707
|
+
private locals: SimulatorVal | undefined[] = []
|
|
708
|
+
|
|
709
|
+
constructor() {
|
|
710
|
+
// TODO
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
get(index: number): SimulatorVal {
|
|
714
|
+
const result = this.locals[`${index}`]
|
|
715
|
+
if (result === undefined) {
|
|
716
|
+
throw new Error(`Local variable at index ${index} is not set`)
|
|
717
|
+
}
|
|
718
|
+
return result
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
set(index: number, val: SimulatorVal): void {
|
|
722
|
+
this.locals[`${index}`] = val
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
private checkedResult(result: SimulatorVal, index: number, expected: string): SimulatorVal {
|
|
726
|
+
if (result.kind.startsWith('Symbol')) {
|
|
727
|
+
if (result.kind !== `Symbol-${expected}`) {
|
|
728
|
+
throw new Error(`Local variable at index ${index} is not a ${expected}`)
|
|
729
|
+
}
|
|
730
|
+
return result
|
|
731
|
+
}
|
|
732
|
+
if (result.kind !== expected) {
|
|
733
|
+
throw new Error(`Local variable at index ${index} is not a ${expected}`)
|
|
734
|
+
}
|
|
735
|
+
return result
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
getBool(index: number): ValBool | SymbolBool {
|
|
739
|
+
const result = this.get(index)
|
|
740
|
+
return this.checkedResult(result, index, 'Bool') as ValBool | SymbolBool
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
getI256(index: number): ValI256 | SymbolI256 {
|
|
744
|
+
const result = this.get(index)
|
|
745
|
+
return this.checkedResult(result, index, 'I256') as ValI256 | SymbolI256
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
getU256(index: number): ValU256 | SymbolU256 {
|
|
749
|
+
const result = this.get(index)
|
|
750
|
+
return this.checkedResult(result, index, 'U256') as ValU256 | SymbolU256
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
getByteVec(index: number): ValByteVec | SymbolByteVec {
|
|
754
|
+
const result = this.get(index)
|
|
755
|
+
return this.checkedResult(result, index, 'ByteVec') as ValByteVec | SymbolByteVec
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
getAddress(index: number): ValAddress | SymbolAddress {
|
|
759
|
+
const result = this.get(index)
|
|
760
|
+
return this.checkedResult(result, index, 'Address') as ValAddress | SymbolAddress
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function unimplemented(instrName: string): never {
|
|
765
|
+
throw new Error(`Unimplemented instruction: ${instrName}`)
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function dummyImplementation(instrName: string): void {
|
|
769
|
+
console.debug(`Dummy implementation for instruction: ${instrName}`)
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
class ApprovedAccumulator {
|
|
773
|
+
private approvedTokens: { id: string; amount: bigint | 'unknown' }[] | 'unknown' = []
|
|
774
|
+
|
|
775
|
+
constructor() {
|
|
776
|
+
this.reset()
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
reset(): void {
|
|
780
|
+
this.approvedTokens = [{ id: ALPH_TOKEN_ID, amount: 0n }]
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
setUnknown(): void {
|
|
784
|
+
this.approvedTokens = 'unknown'
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
getApprovedAttoAlph(): bigint | 'unknown' | undefined {
|
|
788
|
+
if (this.approvedTokens === 'unknown') {
|
|
789
|
+
return 'unknown'
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
const approvedAttoAlph = this.approvedTokens[0].amount
|
|
793
|
+
return approvedAttoAlph === 'unknown' ? 'unknown' : approvedAttoAlph === 0n ? undefined : approvedAttoAlph
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
getApprovedTokens(): { id: string; amount: bigint | 'unknown' }[] | 'unknown' | undefined {
|
|
797
|
+
if (this.approvedTokens === 'unknown') {
|
|
798
|
+
return 'unknown'
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const allTokens = this.approvedTokens.slice(1)
|
|
802
|
+
return allTokens.length === 0 ? undefined : allTokens
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
addApprovedAttoAlph(amount: ValU256 | SymbolU256): void {
|
|
806
|
+
this.addApprovedToken({ kind: 'ByteVec', value: hexToBinUnsafe(ALPH_TOKEN_ID) }, amount)
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
addApprovedToken(tokenId: ValByteVec | SymbolByteVec, amount: ValU256 | SymbolU256): void {
|
|
810
|
+
if (this.approvedTokens === 'unknown') {
|
|
811
|
+
return
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
if (tokenId.kind === 'Symbol-ByteVec') {
|
|
815
|
+
this.approvedTokens = 'unknown'
|
|
816
|
+
return
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const tokenIndex = this.approvedTokens.findIndex((token) => arrayEquals(hexToBinUnsafe(token.id), tokenId.value))
|
|
820
|
+
if (tokenIndex === -1) {
|
|
821
|
+
this.approvedTokens.push({
|
|
822
|
+
id: binToHex(tokenId.value),
|
|
823
|
+
amount: amount.kind === 'Symbol-U256' ? 'unknown' : amount.value
|
|
824
|
+
})
|
|
825
|
+
} else {
|
|
826
|
+
const approved = this.approvedTokens[`${tokenIndex}`]
|
|
827
|
+
if (approved.amount === 'unknown') {
|
|
828
|
+
return
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
if (amount.kind === 'Symbol-U256') {
|
|
832
|
+
approved.amount = 'unknown'
|
|
833
|
+
} else {
|
|
834
|
+
approved.amount += amount.value
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|