@btc-vision/btc-runtime 1.1.4 → 1.1.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btc-vision/btc-runtime",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
4
4
  "description": "Bitcoin Smart Contract Runtime",
5
5
  "main": "btc/index.ts",
6
6
  "types": "btc/index.ts",
@@ -1,200 +1,200 @@
1
- import { Address, ADDRESS_BYTE_LENGTH } from '../types/Address';
2
- import { Selector } from '../math/abi';
3
- import { u256 } from 'as-bignum/assembly';
4
- import { Revert } from '../types/Revert';
5
- import { Map } from '../generic/Map';
6
-
7
- @final
8
- export class BytesReader {
9
- private readonly buffer: DataView;
10
-
11
- private currentOffset: i32 = 0;
12
-
13
- constructor(bytes: Uint8Array) {
14
- this.buffer = new DataView(bytes.buffer);
15
- }
16
-
17
- public readU8(): u8 {
18
- this.verifyEnd(this.currentOffset + 1);
19
-
20
- return this.buffer.getUint8(this.currentOffset++);
21
- }
22
-
23
- public readU16(): u16 {
24
- this.verifyEnd(this.currentOffset + 2);
25
-
26
- const value = this.buffer.getUint16(this.currentOffset, true);
27
- this.currentOffset += 2;
28
-
29
- return value;
30
- }
31
-
32
- public readU32(le: boolean = true): u32 {
33
- this.verifyEnd(this.currentOffset + 4);
34
-
35
- const value = this.buffer.getUint32(this.currentOffset, le);
36
- this.currentOffset += 4;
37
- return value;
38
- }
39
-
40
- public readU64(): u64 {
41
- this.verifyEnd(this.currentOffset + 8);
42
-
43
- const value = this.buffer.getUint64(this.currentOffset, true);
44
- this.currentOffset += 8;
45
-
46
- return value;
47
- }
48
-
49
- public readU256(): u256 {
50
- const next32Bytes: u8[] = [];
51
- for (let i = 0; i < 32; i++) {
52
- next32Bytes[i] = this.readU8();
53
- }
54
-
55
- return u256.fromBytesBE(next32Bytes);
56
- }
57
-
58
- public readBytes(length: u32, zeroStop: boolean = false): Uint8Array {
59
- let bytes: Uint8Array = new Uint8Array(length);
60
- for (let i: u32 = 0; i < length; i++) {
61
- const byte: u8 = this.readU8();
62
- if (zeroStop && byte === 0) {
63
- bytes = bytes.slice(0, i);
64
- break;
65
- }
66
-
67
- bytes[i] = byte;
68
- }
69
-
70
- return bytes;
71
- }
72
-
73
- public readMultiBytesAddressMap(): Map<Address, Uint8Array[]> {
74
- const map: Map<Address, Uint8Array[]> = new Map<Address, Uint8Array[]>();
75
- const size: u8 = this.readU8();
76
-
77
- if (size > 8) throw new Revert('Too many contract called.');
78
-
79
- for (let i: u8 = 0; i < size; i++) {
80
- const address: Address = this.readAddress();
81
- const responseSize: u8 = this.readU8();
82
-
83
- if (responseSize > 10) throw new Revert('Too many calls.');
84
-
85
- const calls: Uint8Array[] = [];
86
- for (let j: u8 = 0; j < responseSize; j++) {
87
- const response: Uint8Array = this.readBytesWithLength();
88
- calls.push(response);
89
- }
90
-
91
- map.set(address, calls);
92
- }
93
-
94
- return map;
95
- }
96
-
97
- public readBytesWithLength(): Uint8Array {
98
- const length = this.readU32();
99
-
100
- return this.readBytes(length);
101
- }
102
-
103
- public readString(length: u16): string {
104
- const bytes = this.readBytes(length, true);
105
-
106
- return String.UTF8.decode(bytes.buffer);
107
- }
108
-
109
- public readTuple(): u256[] {
110
- const length = this.readU32();
111
- const result: u256[] = new Array<u256>(length);
112
-
113
- for (let i = 0; i < length; i++) {
114
- result[i] = this.readU256();
115
- }
116
-
117
- return result;
118
- }
119
-
120
- public readAddressValueTuple(): Map<Address, u256> {
121
- const length: u16 = this.readU16();
122
- const result = new Map<Address, u256>();
123
-
124
- for (let i: u16 = 0; i < length; i++) {
125
- const address = this.readAddress();
126
- const value = this.readU256();
127
-
128
- if (result.has(address)) throw new Revert('Duplicate address found in map');
129
-
130
- result.set(address, value);
131
- }
132
-
133
- return result;
134
- }
135
-
136
- public readSelector(): Selector {
137
- return this.readU32(false);
138
- }
139
-
140
- public readStringWithLength(): string {
141
- const length = this.readU16();
142
-
143
- return this.readString(length);
144
- }
145
-
146
- public readBoolean(): boolean {
147
- return this.readU8() !== 0;
148
- }
149
-
150
- public readFloat(): f32 {
151
- const value = this.buffer.getFloat32(this.currentOffset, true);
152
- this.currentOffset += 4;
153
-
154
- return value;
155
- }
156
-
157
- public readAddress(): Address {
158
- return this.readString(ADDRESS_BYTE_LENGTH);
159
- }
160
-
161
- public getOffset(): i32 {
162
- return this.currentOffset;
163
- }
164
-
165
- public setOffset(offset: i32): void {
166
- this.currentOffset = offset;
167
- }
168
-
169
- public verifyEnd(size: i32): void {
170
- if (this.currentOffset > this.buffer.byteLength) {
171
- throw new Error(`Expected to read ${size} bytes but read ${this.currentOffset} bytes`);
172
- }
173
- }
174
-
175
- public readAddressArray(): Address[] {
176
- const length = this.readU16();
177
- const result = new Array<Address>(length);
178
-
179
- for (let i: u16 = 0; i < length; i++) {
180
- result[i] = this.readAddress();
181
- }
182
-
183
- return result;
184
- }
185
-
186
- private verifyChecksum(): void {
187
- const writtenChecksum = this.readU32();
188
-
189
- let checksum: u32 = 0;
190
- for (let i = 0; i < this.buffer.byteLength; i++) {
191
- checksum += this.buffer.getUint8(i);
192
- }
193
-
194
- checksum = checksum % 2 ** 32;
195
-
196
- if (checksum !== writtenChecksum) {
197
- throw new Error('Invalid checksum for buffer');
198
- }
199
- }
200
- }
1
+ import { Address, ADDRESS_BYTE_LENGTH } from '../types/Address';
2
+ import { Selector } from '../math/abi';
3
+ import { u256 } from 'as-bignum/assembly';
4
+ import { Revert } from '../types/Revert';
5
+ import { Map } from '../generic/Map';
6
+
7
+ @final
8
+ export class BytesReader {
9
+ private readonly buffer: DataView;
10
+
11
+ private currentOffset: i32 = 0;
12
+
13
+ constructor(bytes: Uint8Array) {
14
+ this.buffer = new DataView(bytes.buffer);
15
+ }
16
+
17
+ public readU8(): u8 {
18
+ this.verifyEnd(this.currentOffset + 1);
19
+
20
+ return this.buffer.getUint8(this.currentOffset++);
21
+ }
22
+
23
+ public readU16(): u16 {
24
+ this.verifyEnd(this.currentOffset + 2);
25
+
26
+ const value = this.buffer.getUint16(this.currentOffset, true);
27
+ this.currentOffset += 2;
28
+
29
+ return value;
30
+ }
31
+
32
+ public readU32(le: boolean = true): u32 {
33
+ this.verifyEnd(this.currentOffset + 4);
34
+
35
+ const value = this.buffer.getUint32(this.currentOffset, le);
36
+ this.currentOffset += 4;
37
+ return value;
38
+ }
39
+
40
+ public readU64(): u64 {
41
+ this.verifyEnd(this.currentOffset + 8);
42
+
43
+ const value = this.buffer.getUint64(this.currentOffset, true);
44
+ this.currentOffset += 8;
45
+
46
+ return value;
47
+ }
48
+
49
+ public readU256(): u256 {
50
+ const next32Bytes: u8[] = [];
51
+ for (let i = 0; i < 32; i++) {
52
+ next32Bytes[i] = this.readU8();
53
+ }
54
+
55
+ return u256.fromBytesBE(next32Bytes);
56
+ }
57
+
58
+ public readBytes(length: u32, zeroStop: boolean = false): Uint8Array {
59
+ let bytes: Uint8Array = new Uint8Array(length);
60
+ for (let i: u32 = 0; i < length; i++) {
61
+ const byte: u8 = this.readU8();
62
+ if (zeroStop && byte === 0) {
63
+ bytes = bytes.slice(0, i);
64
+ break;
65
+ }
66
+
67
+ bytes[i] = byte;
68
+ }
69
+
70
+ return bytes;
71
+ }
72
+
73
+ public readMultiBytesAddressMap(): Map<Address, Uint8Array[]> {
74
+ const map: Map<Address, Uint8Array[]> = new Map<Address, Uint8Array[]>();
75
+ const size: u8 = this.readU8();
76
+
77
+ if (size > 8) throw new Revert('Too many contract called.');
78
+
79
+ for (let i: u8 = 0; i < size; i++) {
80
+ const address: Address = this.readAddress();
81
+ const responseSize: u8 = this.readU8();
82
+
83
+ if (responseSize > 10) throw new Revert('Too many calls.');
84
+
85
+ const calls: Uint8Array[] = [];
86
+ for (let j: u8 = 0; j < responseSize; j++) {
87
+ const response: Uint8Array = this.readBytesWithLength();
88
+ calls.push(response);
89
+ }
90
+
91
+ map.set(address, calls);
92
+ }
93
+
94
+ return map;
95
+ }
96
+
97
+ public readBytesWithLength(): Uint8Array {
98
+ const length = this.readU32();
99
+
100
+ return this.readBytes(length);
101
+ }
102
+
103
+ public readString(length: u16): string {
104
+ const bytes = this.readBytes(length, true);
105
+
106
+ return String.UTF8.decode(bytes.buffer);
107
+ }
108
+
109
+ public readTuple(): u256[] {
110
+ const length = this.readU32();
111
+ const result: u256[] = new Array<u256>(length);
112
+
113
+ for (let i: u32 = 0; i < length; i++) {
114
+ result[i] = this.readU256();
115
+ }
116
+
117
+ return result;
118
+ }
119
+
120
+ public readAddressValueTuple(): Map<Address, u256> {
121
+ const length: u16 = this.readU16();
122
+ const result = new Map<Address, u256>();
123
+
124
+ for (let i: u16 = 0; i < length; i++) {
125
+ const address = this.readAddress();
126
+ const value = this.readU256();
127
+
128
+ if (result.has(address)) throw new Revert('Duplicate address found in map');
129
+
130
+ result.set(address, value);
131
+ }
132
+
133
+ return result;
134
+ }
135
+
136
+ public readSelector(): Selector {
137
+ return this.readU32(false);
138
+ }
139
+
140
+ public readStringWithLength(): string {
141
+ const length = this.readU16();
142
+
143
+ return this.readString(length);
144
+ }
145
+
146
+ public readBoolean(): boolean {
147
+ return this.readU8() !== 0;
148
+ }
149
+
150
+ public readFloat(): f32 {
151
+ const value = this.buffer.getFloat32(this.currentOffset, true);
152
+ this.currentOffset += 4;
153
+
154
+ return value;
155
+ }
156
+
157
+ public readAddress(): Address {
158
+ return this.readString(ADDRESS_BYTE_LENGTH);
159
+ }
160
+
161
+ public getOffset(): i32 {
162
+ return this.currentOffset;
163
+ }
164
+
165
+ public setOffset(offset: i32): void {
166
+ this.currentOffset = offset;
167
+ }
168
+
169
+ public verifyEnd(size: i32): void {
170
+ if (this.currentOffset > this.buffer.byteLength) {
171
+ throw new Error(`Expected to read ${size} bytes but read ${this.currentOffset} bytes`);
172
+ }
173
+ }
174
+
175
+ public readAddressArray(): Address[] {
176
+ const length = this.readU16();
177
+ const result = new Array<Address>(length);
178
+
179
+ for (let i: u16 = 0; i < length; i++) {
180
+ result[i] = this.readAddress();
181
+ }
182
+
183
+ return result;
184
+ }
185
+
186
+ private verifyChecksum(): void {
187
+ const writtenChecksum = this.readU32();
188
+
189
+ let checksum: u32 = 0;
190
+ for (let i = 0; i < this.buffer.byteLength; i++) {
191
+ checksum += this.buffer.getUint8(i);
192
+ }
193
+
194
+ checksum = checksum % 2 ** 32;
195
+
196
+ if (checksum !== writtenChecksum) {
197
+ throw new Error('Invalid checksum for buffer');
198
+ }
199
+ }
200
+ }
@@ -9,6 +9,7 @@ import { cyrb53a } from '../math/cyrb53';
9
9
  import { Revert } from '../types/Revert';
10
10
  import { Map } from '../generic/Map';
11
11
  import { BlockchainStorage, PointerStorage } from '../types';
12
+ import { ArrayBuffer } from 'arraybuffer';
12
13
 
13
14
  export enum BufferDataType {
14
15
  U8 = 0,
@@ -32,7 +33,9 @@ export class BytesWriter {
32
33
  length: i32 = 1,
33
34
  private readonly trackDataTypes: boolean = false,
34
35
  ) {
35
- this.buffer = new DataView(new ArrayBuffer(length));
36
+ const arrayBuffer = new ArrayBuffer(length);
37
+
38
+ this.buffer = new DataView(arrayBuffer);
36
39
  }
37
40
 
38
41
  public bufferLength(): u32 {
@@ -239,11 +242,12 @@ export class BytesWriter {
239
242
  }
240
243
  }
241
244
 
242
- public getBuffer(clear: boolean = true): Uint8Array {
243
- const buf = new Uint8Array(this.buffer.byteLength);
244
- for (let i: u32 = 0; i < u32(this.buffer.byteLength); i++) {
245
- buf[i] = this.buffer.getUint8(i);
246
- }
245
+ public getBuffer(clear: boolean = false): Uint8Array {
246
+ const buf = Uint8Array.wrap(
247
+ this.buffer.buffer,
248
+ this.buffer.byteOffset,
249
+ this.buffer.byteLength,
250
+ );
247
251
 
248
252
  if (clear) this.clear();
249
253
 
@@ -94,7 +94,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
94
94
  throw new Revert('Already initialized');
95
95
  }
96
96
 
97
- if (!skipOwnerVerification) this.onlyOwner(Blockchain.origin);
97
+ if (!skipOwnerVerification) this.onlyOwner(Blockchain.txOrigin);
98
98
 
99
99
  if (params.decimals > 32) {
100
100
  throw new Revert('Decimals can not be more than 32');
@@ -117,15 +117,16 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
117
117
  }
118
118
 
119
119
  public approve(callData: Calldata): BytesWriter {
120
- const response = new BytesWriter();
121
-
120
+ // Define the owner and spender
121
+ const owner = Blockchain.msgSender;
122
122
  const spender: Address = callData.readAddress();
123
123
  const value = callData.readU256();
124
124
 
125
- const resp = this._approve(spender, value);
126
- response.writeBoolean(resp);
125
+ // Response buffer
126
+ const response = new BytesWriter();
127
127
 
128
- this.createApproveEvent(Blockchain.origin, spender, value);
128
+ const resp = this._approve(owner, spender, value);
129
+ response.writeBoolean(resp);
129
130
 
130
131
  return response;
131
132
  }
@@ -233,12 +234,16 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
233
234
  return senderMap.get(spender);
234
235
  }
235
236
 
236
- protected _approve(spender: Address, value: u256): boolean {
237
- const callee = Blockchain.origin;
237
+ protected _approve(owner: Address, spender: Address, value: u256): boolean {
238
+ if (owner === Blockchain.DEAD_ADDRESS || spender === Blockchain.DEAD_ADDRESS) {
239
+ throw new Revert('Cannot approve from or to dead address');
240
+ }
238
241
 
239
- const senderMap = this.allowanceMap.get(callee);
242
+ const senderMap = this.allowanceMap.get(owner);
240
243
  senderMap.set(spender, value);
241
244
 
245
+ this.createApproveEvent(owner, spender, value);
246
+
242
247
  return true;
243
248
  }
244
249
 
@@ -254,19 +259,16 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
254
259
  throw new Revert(`No tokens`);
255
260
  }
256
261
 
257
- const callee = Blockchain.origin;
258
- const caller = Blockchain.sender;
259
-
260
- if (onlyOwner) this.onlyOwner(callee); // only indexers can burn tokens
262
+ if (onlyOwner) this.onlyOwner(Blockchain.txOrigin); // only indexers can burn tokens
261
263
 
262
264
  if (this._totalSupply.value < value) throw new Revert(`Insufficient total supply.`);
263
- if (!this.balanceOfMap.has(caller)) throw new Revert('Empty');
265
+ if (!this.balanceOfMap.has(Blockchain.msgSender)) throw new Revert('No balance');
264
266
 
265
- const balance: u256 = this.balanceOfMap.get(caller);
267
+ const balance: u256 = this.balanceOfMap.get(Blockchain.msgSender);
266
268
  if (balance < value) throw new Revert(`Insufficient balance`);
267
269
 
268
270
  const newBalance: u256 = SafeMath.sub(balance, value);
269
- this.balanceOfMap.set(caller, newBalance);
271
+ this.balanceOfMap.set(Blockchain.msgSender, newBalance);
270
272
 
271
273
  // @ts-ignore
272
274
  this._totalSupply -= value;
@@ -276,9 +278,7 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
276
278
  }
277
279
 
278
280
  protected _mint(to: Address, value: u256, onlyOwner: boolean = true): boolean {
279
- const callee = Blockchain.origin;
280
-
281
- if (onlyOwner) this.onlyOwner(callee);
281
+ if (onlyOwner) this.onlyOwner(Blockchain.txOrigin);
282
282
 
283
283
  if (!this.balanceOfMap.has(to)) {
284
284
  this.balanceOfMap.set(to, value);
@@ -299,31 +299,27 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
299
299
  }
300
300
 
301
301
  protected _transfer(to: string, value: u256): boolean {
302
- const caller = Blockchain.origin;
303
-
304
- if (!this.balanceOfMap.has(caller)) throw new Revert();
305
- if (this.isSelf(caller)) throw new Revert('Can not transfer from self account');
302
+ const sender = Blockchain.msgSender;
306
303
 
307
- if (caller === to) {
308
- throw new Revert(`Cannot transfer to self`);
309
- }
304
+ if (!this.balanceOfMap.has(sender)) throw new Revert();
305
+ if (this.isSelf(sender)) throw new Revert('Can not transfer from self account');
310
306
 
311
307
  if (u256.eq(value, u256.Zero)) {
312
308
  throw new Revert(`Cannot transfer 0 tokens`);
313
309
  }
314
310
 
315
- const balance: u256 = this.balanceOfMap.get(caller);
311
+ const balance: u256 = this.balanceOfMap.get(sender);
316
312
  if (balance < value) throw new Revert(`Insufficient balance`);
317
313
 
318
314
  const newBalance: u256 = SafeMath.sub(balance, value);
319
- this.balanceOfMap.set(caller, newBalance);
315
+ this.balanceOfMap.set(sender, newBalance);
320
316
 
321
317
  const toBalance: u256 = this.balanceOfMap.get(to);
322
318
  const newToBalance: u256 = SafeMath.add(toBalance, value);
323
319
 
324
320
  this.balanceOfMap.set(to, newToBalance);
325
321
 
326
- this.createTransferEvent(caller, to, value);
322
+ this.createTransferEvent(sender, to, value);
327
323
 
328
324
  return true;
329
325
  }
@@ -337,7 +333,6 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
337
333
  );
338
334
 
339
335
  const newBalance: u256 = SafeMath.sub(balance, value);
340
-
341
336
  this.balanceOfMap.set(from, newBalance);
342
337
 
343
338
  if (!this.balanceOfMap.has(to)) {
@@ -355,22 +350,30 @@ export abstract class DeployableOP_20 extends OP_NET implements IOP_20 {
355
350
  }
356
351
 
357
352
  protected _transferFrom(from: Address, to: Address, value: u256): boolean {
358
- const spender = Blockchain.origin;
353
+ if (to === Blockchain.DEAD_ADDRESS || from === Blockchain.DEAD_ADDRESS) {
354
+ throw new Revert('Cannot transfer to or from dead address');
355
+ }
359
356
 
360
- if (this.isSelf(spender)) throw new Revert('Can not transfer from self account');
357
+ this._spendAllowance(from, Blockchain.msgSender, value);
358
+ this._unsafeTransferFrom(from, to, value);
361
359
 
362
- const fromAllowanceMap = this.allowanceMap.get(from);
363
- const allowed: u256 = fromAllowanceMap.get(spender);
364
- if (allowed < value) throw new Revert(`Insufficient allowance ${allowed} < ${value}`);
360
+ return true;
361
+ }
365
362
 
366
- const newAllowance: u256 = SafeMath.sub(allowed, value);
367
- fromAllowanceMap.set(spender, newAllowance);
363
+ protected _spendAllowance(owner: Address, spender: Address, value: u256): void {
364
+ const ownerAllowanceMap = this.allowanceMap.get(owner);
365
+ const allowed: u256 = ownerAllowanceMap.get(spender);
368
366
 
369
- this.allowanceMap.set(from, fromAllowanceMap);
367
+ if (allowed < value) {
368
+ throw new Revert(
369
+ `Insufficient allowance ${allowed} < ${value}. Spender: ${spender} - Owner: ${owner}`,
370
+ );
371
+ }
370
372
 
371
- this._unsafeTransferFrom(from, to, value);
373
+ const newAllowance: u256 = SafeMath.sub(allowed, value);
374
+ ownerAllowanceMap.set(spender, newAllowance);
372
375
 
373
- return true;
376
+ this.allowanceMap.set(owner, ownerAllowanceMap);
374
377
  }
375
378
 
376
379
  protected createBurnEvent(value: u256): void {
@@ -28,7 +28,7 @@ export * from '../env/global';
28
28
  export class BlockchainEnvironment {
29
29
  private static readonly MAX_U16: u16 = 65535;
30
30
  private static readonly runtimeException: string = 'RuntimeException';
31
-
31
+ public readonly DEAD_ADDRESS: Address = 'bc1dead';
32
32
  private storage: PointerStorage = new MapU256();
33
33
  private events: NetEvent[] = [];
34
34
  private currentBlock: u256 = u256.Zero;
@@ -37,24 +37,24 @@ export class BlockchainEnvironment {
37
37
 
38
38
  constructor() {}
39
39
 
40
- private _origin: PotentialAddress = null;
40
+ private _txOrigin: PotentialAddress = null;
41
41
 
42
- public get origin(): Address {
43
- if (!this._origin) {
42
+ public get txOrigin(): Address {
43
+ if (!this._txOrigin) {
44
44
  throw this.error('Callee is required');
45
45
  }
46
46
 
47
- return this._origin as Address;
47
+ return this._txOrigin as Address;
48
48
  }
49
49
 
50
- private _sender: PotentialAddress = null;
50
+ private _msgSender: PotentialAddress = null;
51
51
 
52
- public get sender(): Address {
53
- if (!this._sender) {
52
+ public get msgSender(): Address {
53
+ if (!this._msgSender) {
54
54
  throw this.error('Caller is required');
55
55
  }
56
56
 
57
- return this._sender as Address;
57
+ return this._msgSender as Address;
58
58
  }
59
59
 
60
60
  private _timestamp: u64 = 0;
@@ -124,8 +124,8 @@ export class BlockchainEnvironment {
124
124
  public setEnvironment(data: Uint8Array): void {
125
125
  const reader: BytesReader = new BytesReader(data);
126
126
 
127
- this._sender = reader.readAddress();
128
- this._origin = reader.readAddress();
127
+ this._msgSender = reader.readAddress();
128
+ this._txOrigin = reader.readAddress(); // "leftmost thing in the call chain"
129
129
  this.currentBlock = reader.readU256();
130
130
 
131
131
  this._owner = reader.readAddress();
@@ -137,7 +137,7 @@ export class BlockchainEnvironment {
137
137
  }
138
138
 
139
139
  public call(destinationContract: Address, calldata: BytesWriter): BytesReader {
140
- if (destinationContract === this._origin) {
140
+ if (destinationContract === this._contractAddress) {
141
141
  throw this.error('Cannot call self');
142
142
  }
143
143