@btc-vision/btc-runtime 1.10.6 → 1.10.7
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/LICENSE.md +1 -1
- package/package.json +1 -1
- package/runtime/contracts/OP20S.ts +117 -78
- package/runtime/events/op20s/OP20SEvents.ts +60 -0
- package/runtime/index.ts +1 -0
package/LICENSE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2025 [Orange Pill Labs Holding Ltd.]
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/package.json
CHANGED
|
@@ -5,83 +5,51 @@ import { Calldata } from '../types';
|
|
|
5
5
|
import { Address } from '../types/Address';
|
|
6
6
|
import { Revert } from '../types/Revert';
|
|
7
7
|
import { StoredU256 } from '../storage/StoredU256';
|
|
8
|
-
import { StoredAddress } from '../storage/StoredAddress';
|
|
9
8
|
import { EMPTY_POINTER } from '../math/bytes';
|
|
10
9
|
import { OP20 } from './OP20';
|
|
11
|
-
import { IOP20S } from './interfaces/IOP20S';
|
|
12
|
-
import { OP20InitParameters } from './interfaces/OP20InitParameters';
|
|
13
|
-
import { ADDRESS_BYTE_LENGTH, U256_BYTE_LENGTH, U64_BYTE_LENGTH } from '../utils';
|
|
14
10
|
import { Selector } from '../math/abi';
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
import { AddressMemoryMap } from '../memory/AddressMemoryMap';
|
|
12
|
+
import {
|
|
13
|
+
MaxStalenessUpdatedEvent,
|
|
14
|
+
PegAuthorityRenouncedEvent,
|
|
15
|
+
PegAuthorityTransferredEvent,
|
|
16
|
+
PegAuthorityTransferStartedEvent,
|
|
17
|
+
PegRateUpdatedEvent,
|
|
18
|
+
} from '../events/op20s/OP20SEvents';
|
|
19
|
+
|
|
20
|
+
// Selectors: sha256 first 4 bytes
|
|
23
21
|
export const PEG_RATE_SELECTOR: u32 = 0x4d1f6caf;
|
|
24
|
-
|
|
25
|
-
// "pegAuthority()"
|
|
26
22
|
export const PEG_AUTHORITY_SELECTOR: u32 = 0xd767a583;
|
|
27
|
-
|
|
28
|
-
// "pegUpdatedAt()"
|
|
29
23
|
export const PEG_UPDATED_AT_SELECTOR: u32 = 0x1e99d1a1;
|
|
30
|
-
|
|
31
|
-
// "maxStaleness()"
|
|
32
24
|
export const MAX_STALENESS_SELECTOR: u32 = 0x0b17a602;
|
|
33
|
-
|
|
34
|
-
// "isStale()"
|
|
35
25
|
export const IS_STALE_SELECTOR: u32 = 0x147c08ef;
|
|
36
26
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
27
|
+
const pegRatePointer: u16 = Blockchain.nextPointer;
|
|
28
|
+
const pegAuthorityPointer: u16 = Blockchain.nextPointer;
|
|
29
|
+
const pegUpdatedAtPointer: u16 = Blockchain.nextPointer;
|
|
30
|
+
const maxStalenessPointer: u16 = Blockchain.nextPointer;
|
|
31
|
+
const pendingAuthorityPointer: u16 = Blockchain.nextPointer;
|
|
42
32
|
|
|
43
|
-
export abstract class OP20S extends OP20
|
|
33
|
+
export abstract class OP20S extends OP20 {
|
|
44
34
|
protected readonly _pegRate: StoredU256;
|
|
45
|
-
protected readonly
|
|
35
|
+
protected readonly _pegAuthorityMap: AddressMemoryMap;
|
|
46
36
|
protected readonly _pegUpdatedAt: StoredU256;
|
|
47
37
|
protected readonly _maxStaleness: StoredU256;
|
|
48
|
-
protected readonly
|
|
38
|
+
protected readonly _pendingAuthorityMap: AddressMemoryMap;
|
|
49
39
|
|
|
50
40
|
public constructor() {
|
|
51
41
|
super();
|
|
52
42
|
this._pegRate = new StoredU256(pegRatePointer, EMPTY_POINTER);
|
|
53
|
-
this.
|
|
43
|
+
this._pegAuthorityMap = new AddressMemoryMap(pegAuthorityPointer);
|
|
54
44
|
this._pegUpdatedAt = new StoredU256(pegUpdatedAtPointer, EMPTY_POINTER);
|
|
55
45
|
this._maxStaleness = new StoredU256(maxStalenessPointer, EMPTY_POINTER);
|
|
56
|
-
this.
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public override instantiate(
|
|
60
|
-
params: OP20SInitParameters,
|
|
61
|
-
skipDeployerVerification: boolean = false,
|
|
62
|
-
): void {
|
|
63
|
-
super.instantiate(params, skipDeployerVerification);
|
|
64
|
-
|
|
65
|
-
if (params.pegAuthority === Address.zero()) {
|
|
66
|
-
throw new Revert('Invalid peg authority');
|
|
67
|
-
}
|
|
68
|
-
if (params.initialPegRate.isZero()) {
|
|
69
|
-
throw new Revert('Invalid initial peg rate');
|
|
70
|
-
}
|
|
71
|
-
if (params.maxStaleness === 0) {
|
|
72
|
-
throw new Revert('Invalid max staleness');
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
this._pegAuthority.value = params.pegAuthority;
|
|
76
|
-
this._pegRate.value = params.initialPegRate;
|
|
77
|
-
this._pegUpdatedAt.value = u256.fromU64(Blockchain.block.number);
|
|
78
|
-
this._maxStaleness.value = u256.fromU64(params.maxStaleness);
|
|
46
|
+
this._pendingAuthorityMap = new AddressMemoryMap(pendingAuthorityPointer);
|
|
79
47
|
}
|
|
80
48
|
|
|
81
49
|
@method()
|
|
82
50
|
@returns({ name: 'rate', type: ABIDataTypes.UINT256 })
|
|
83
51
|
public pegRate(_: Calldata): BytesWriter {
|
|
84
|
-
const w = new BytesWriter(
|
|
52
|
+
const w = new BytesWriter(32);
|
|
85
53
|
w.writeU256(this._pegRate.value);
|
|
86
54
|
return w;
|
|
87
55
|
}
|
|
@@ -89,15 +57,15 @@ export abstract class OP20S extends OP20 implements IOP20S {
|
|
|
89
57
|
@method()
|
|
90
58
|
@returns({ name: 'authority', type: ABIDataTypes.ADDRESS })
|
|
91
59
|
public pegAuthority(_: Calldata): BytesWriter {
|
|
92
|
-
const w = new BytesWriter(
|
|
93
|
-
w.writeAddress(this.
|
|
60
|
+
const w = new BytesWriter(32);
|
|
61
|
+
w.writeAddress(this._getPegAuthority());
|
|
94
62
|
return w;
|
|
95
63
|
}
|
|
96
64
|
|
|
97
65
|
@method()
|
|
98
66
|
@returns({ name: 'updatedAt', type: ABIDataTypes.UINT64 })
|
|
99
67
|
public pegUpdatedAt(_: Calldata): BytesWriter {
|
|
100
|
-
const w = new BytesWriter(
|
|
68
|
+
const w = new BytesWriter(8);
|
|
101
69
|
w.writeU64(this._pegUpdatedAt.value.toU64());
|
|
102
70
|
return w;
|
|
103
71
|
}
|
|
@@ -105,7 +73,7 @@ export abstract class OP20S extends OP20 implements IOP20S {
|
|
|
105
73
|
@method()
|
|
106
74
|
@returns({ name: 'staleness', type: ABIDataTypes.UINT64 })
|
|
107
75
|
public maxStaleness(_: Calldata): BytesWriter {
|
|
108
|
-
const w = new BytesWriter(
|
|
76
|
+
const w = new BytesWriter(8);
|
|
109
77
|
w.writeU64(this._maxStaleness.value.toU64());
|
|
110
78
|
return w;
|
|
111
79
|
}
|
|
@@ -119,74 +87,115 @@ export abstract class OP20S extends OP20 implements IOP20S {
|
|
|
119
87
|
}
|
|
120
88
|
|
|
121
89
|
@method({ name: 'newRate', type: ABIDataTypes.UINT256 })
|
|
90
|
+
@emit('PegRateUpdated')
|
|
122
91
|
public updatePegRate(calldata: Calldata): BytesWriter {
|
|
123
|
-
this.
|
|
92
|
+
this._onlyPegAuthority();
|
|
124
93
|
|
|
125
94
|
const newRate = calldata.readU256();
|
|
126
95
|
if (newRate.isZero()) {
|
|
127
96
|
throw new Revert('Invalid peg rate');
|
|
128
97
|
}
|
|
129
98
|
|
|
99
|
+
const oldRate = this._pegRate.value;
|
|
100
|
+
const blockNumber = Blockchain.block.number;
|
|
101
|
+
|
|
130
102
|
this._pegRate.value = newRate;
|
|
131
|
-
this._pegUpdatedAt.value = u256.fromU64(
|
|
103
|
+
this._pegUpdatedAt.value = u256.fromU64(blockNumber);
|
|
104
|
+
|
|
105
|
+
this.emitEvent(new PegRateUpdatedEvent(oldRate, newRate, blockNumber));
|
|
132
106
|
|
|
133
107
|
return new BytesWriter(0);
|
|
134
108
|
}
|
|
135
109
|
|
|
136
110
|
@method({ name: 'newStaleness', type: ABIDataTypes.UINT64 })
|
|
111
|
+
@emit('MaxStalenessUpdated')
|
|
137
112
|
public updateMaxStaleness(calldata: Calldata): BytesWriter {
|
|
138
|
-
this.
|
|
113
|
+
this._onlyPegAuthority();
|
|
139
114
|
|
|
140
115
|
const newStaleness = calldata.readU64();
|
|
141
|
-
if (newStaleness
|
|
116
|
+
if (newStaleness == 0) {
|
|
142
117
|
throw new Revert('Invalid max staleness');
|
|
143
118
|
}
|
|
144
119
|
|
|
120
|
+
const oldStaleness = this._maxStaleness.value.toU64();
|
|
145
121
|
this._maxStaleness.value = u256.fromU64(newStaleness);
|
|
146
122
|
|
|
123
|
+
this.emitEvent(new MaxStalenessUpdatedEvent(oldStaleness, newStaleness));
|
|
124
|
+
|
|
147
125
|
return new BytesWriter(0);
|
|
148
126
|
}
|
|
149
127
|
|
|
150
128
|
@method({ name: 'newAuthority', type: ABIDataTypes.ADDRESS })
|
|
129
|
+
@emit('PegAuthorityTransferStarted')
|
|
151
130
|
public transferPegAuthority(calldata: Calldata): BytesWriter {
|
|
152
|
-
this.
|
|
131
|
+
this._onlyPegAuthority();
|
|
153
132
|
|
|
154
133
|
const newAuthority = calldata.readAddress();
|
|
155
|
-
if (newAuthority
|
|
134
|
+
if (newAuthority.equals(Address.zero())) {
|
|
156
135
|
throw new Revert('Invalid new authority');
|
|
157
136
|
}
|
|
158
137
|
|
|
159
|
-
|
|
138
|
+
const currentAuthority = this._getPegAuthority();
|
|
139
|
+
this._setPendingAuthority(newAuthority);
|
|
140
|
+
|
|
141
|
+
this.emitEvent(new PegAuthorityTransferStartedEvent(currentAuthority, newAuthority));
|
|
160
142
|
|
|
161
143
|
return new BytesWriter(0);
|
|
162
144
|
}
|
|
163
145
|
|
|
164
146
|
@method()
|
|
147
|
+
@emit('PegAuthorityTransferred')
|
|
165
148
|
public acceptPegAuthority(_: Calldata): BytesWriter {
|
|
166
|
-
const pending = this.
|
|
167
|
-
if (pending
|
|
149
|
+
const pending = this._getPendingAuthority();
|
|
150
|
+
if (pending.equals(Address.zero())) {
|
|
168
151
|
throw new Revert('No pending authority');
|
|
169
152
|
}
|
|
170
153
|
if (!Blockchain.tx.sender.equals(pending)) {
|
|
171
154
|
throw new Revert('Not pending authority');
|
|
172
155
|
}
|
|
173
156
|
|
|
174
|
-
|
|
175
|
-
this.
|
|
157
|
+
const previousAuthority = this._getPegAuthority();
|
|
158
|
+
this._setPegAuthority(pending);
|
|
159
|
+
this._setPendingAuthority(Address.zero());
|
|
160
|
+
|
|
161
|
+
this.emitEvent(new PegAuthorityTransferredEvent(previousAuthority, pending));
|
|
176
162
|
|
|
177
163
|
return new BytesWriter(0);
|
|
178
164
|
}
|
|
179
165
|
|
|
180
166
|
@method()
|
|
167
|
+
@emit('PegAuthorityRenounced')
|
|
181
168
|
public renouncePegAuthority(_: Calldata): BytesWriter {
|
|
182
|
-
this.
|
|
169
|
+
this._onlyPegAuthority();
|
|
170
|
+
|
|
171
|
+
const previousAuthority = this._getPegAuthority();
|
|
172
|
+
this._setPegAuthority(Address.zero());
|
|
173
|
+
this._setPendingAuthority(Address.zero());
|
|
183
174
|
|
|
184
|
-
this.
|
|
185
|
-
this._pendingAuthority.value = Address.zero();
|
|
175
|
+
this.emitEvent(new PegAuthorityRenouncedEvent(previousAuthority));
|
|
186
176
|
|
|
187
177
|
return new BytesWriter(0);
|
|
188
178
|
}
|
|
189
179
|
|
|
180
|
+
protected initializePeg(pegAuthority: Address, initialPegRate: u256, maxStaleness: u64): void {
|
|
181
|
+
if (pegAuthority.equals(Address.zero())) {
|
|
182
|
+
throw new Revert('Invalid peg authority');
|
|
183
|
+
}
|
|
184
|
+
if (initialPegRate.isZero()) {
|
|
185
|
+
throw new Revert('Invalid initial peg rate');
|
|
186
|
+
}
|
|
187
|
+
if (maxStaleness == 0) {
|
|
188
|
+
throw new Revert('Invalid max staleness');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
this._setPegAuthority(pegAuthority);
|
|
192
|
+
this._pegRate.value = initialPegRate;
|
|
193
|
+
this._pegUpdatedAt.value = u256.fromU64(Blockchain.block.number);
|
|
194
|
+
this._maxStaleness.value = u256.fromU64(maxStaleness);
|
|
195
|
+
|
|
196
|
+
this.emitEvent(new PegRateUpdatedEvent(u256.Zero, initialPegRate, Blockchain.block.number));
|
|
197
|
+
}
|
|
198
|
+
|
|
190
199
|
protected _isStale(): boolean {
|
|
191
200
|
const currentBlock = Blockchain.block.number;
|
|
192
201
|
const updatedAt = this._pegUpdatedAt.value.toU64();
|
|
@@ -201,19 +210,49 @@ export abstract class OP20S extends OP20 implements IOP20S {
|
|
|
201
210
|
}
|
|
202
211
|
}
|
|
203
212
|
|
|
204
|
-
protected
|
|
205
|
-
if (!Blockchain.tx.sender.equals(this.
|
|
213
|
+
protected _onlyPegAuthority(): void {
|
|
214
|
+
if (!Blockchain.tx.sender.equals(this._getPegAuthority())) {
|
|
206
215
|
throw new Revert('Not peg authority');
|
|
207
216
|
}
|
|
208
217
|
}
|
|
209
218
|
|
|
219
|
+
protected _getPegAuthority(): Address {
|
|
220
|
+
const stored = this._pegAuthorityMap.get(Address.zero());
|
|
221
|
+
if (stored.isZero()) return Address.zero();
|
|
222
|
+
return this._u256ToAddress(stored);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
protected _setPegAuthority(addr: Address): void {
|
|
226
|
+
this._pegAuthorityMap.set(Address.zero(), this._addressToU256(addr));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
protected _getPendingAuthority(): Address {
|
|
230
|
+
const stored = this._pendingAuthorityMap.get(Address.zero());
|
|
231
|
+
if (stored.isZero()) return Address.zero();
|
|
232
|
+
return this._u256ToAddress(stored);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
protected _setPendingAuthority(addr: Address): void {
|
|
236
|
+
this._pendingAuthorityMap.set(Address.zero(), this._addressToU256(addr));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
protected _addressToU256(addr: Address): u256 {
|
|
240
|
+
return u256.fromUint8ArrayBE(addr);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
protected _u256ToAddress(val: u256): Address {
|
|
244
|
+
if (val.isZero()) return Address.zero();
|
|
245
|
+
const bytes = val.toUint8Array(true);
|
|
246
|
+
return Address.fromUint8Array(bytes);
|
|
247
|
+
}
|
|
248
|
+
|
|
210
249
|
protected override isSelectorExcluded(selector: Selector): boolean {
|
|
211
250
|
if (
|
|
212
|
-
selector
|
|
213
|
-
selector
|
|
214
|
-
selector
|
|
215
|
-
selector
|
|
216
|
-
selector
|
|
251
|
+
selector == PEG_RATE_SELECTOR ||
|
|
252
|
+
selector == PEG_AUTHORITY_SELECTOR ||
|
|
253
|
+
selector == PEG_UPDATED_AT_SELECTOR ||
|
|
254
|
+
selector == MAX_STALENESS_SELECTOR ||
|
|
255
|
+
selector == IS_STALE_SELECTOR
|
|
217
256
|
) {
|
|
218
257
|
return true;
|
|
219
258
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { u256 } from '@btc-vision/as-bignum/assembly';
|
|
2
|
+
import { NetEvent } from '../NetEvent';
|
|
3
|
+
import { BytesWriter } from '../../buffer/BytesWriter';
|
|
4
|
+
import { ADDRESS_BYTE_LENGTH, U256_BYTE_LENGTH, U64_BYTE_LENGTH } from '../../utils';
|
|
5
|
+
import { Address } from '../../types/Address';
|
|
6
|
+
|
|
7
|
+
@final
|
|
8
|
+
export class PegRateUpdatedEvent extends NetEvent {
|
|
9
|
+
constructor(oldRate: u256, newRate: u256, updatedAt: u64) {
|
|
10
|
+
const data: BytesWriter = new BytesWriter(U256_BYTE_LENGTH * 2 + U64_BYTE_LENGTH);
|
|
11
|
+
data.writeU256(oldRate);
|
|
12
|
+
data.writeU256(newRate);
|
|
13
|
+
data.writeU64(updatedAt);
|
|
14
|
+
|
|
15
|
+
super('PegRateUpdated', data);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@final
|
|
20
|
+
export class PegAuthorityTransferStartedEvent extends NetEvent {
|
|
21
|
+
constructor(currentAuthority: Address, pendingAuthority: Address) {
|
|
22
|
+
const data: BytesWriter = new BytesWriter(ADDRESS_BYTE_LENGTH * 2);
|
|
23
|
+
data.writeAddress(currentAuthority);
|
|
24
|
+
data.writeAddress(pendingAuthority);
|
|
25
|
+
|
|
26
|
+
super('PegAuthorityTransferStarted', data);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@final
|
|
31
|
+
export class PegAuthorityTransferredEvent extends NetEvent {
|
|
32
|
+
constructor(previousAuthority: Address, newAuthority: Address) {
|
|
33
|
+
const data: BytesWriter = new BytesWriter(ADDRESS_BYTE_LENGTH * 2);
|
|
34
|
+
data.writeAddress(previousAuthority);
|
|
35
|
+
data.writeAddress(newAuthority);
|
|
36
|
+
|
|
37
|
+
super('PegAuthorityTransferred', data);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@final
|
|
42
|
+
export class PegAuthorityRenouncedEvent extends NetEvent {
|
|
43
|
+
constructor(previousAuthority: Address) {
|
|
44
|
+
const data: BytesWriter = new BytesWriter(ADDRESS_BYTE_LENGTH);
|
|
45
|
+
data.writeAddress(previousAuthority);
|
|
46
|
+
|
|
47
|
+
super('PegAuthorityRenounced', data);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@final
|
|
52
|
+
export class MaxStalenessUpdatedEvent extends NetEvent {
|
|
53
|
+
constructor(oldStaleness: u64, newStaleness: u64) {
|
|
54
|
+
const data: BytesWriter = new BytesWriter(U64_BYTE_LENGTH * 2);
|
|
55
|
+
data.writeU64(oldStaleness);
|
|
56
|
+
data.writeU64(newStaleness);
|
|
57
|
+
|
|
58
|
+
super('MaxStalenessUpdated', data);
|
|
59
|
+
}
|
|
60
|
+
}
|
package/runtime/index.ts
CHANGED